liusheng
9 天以前 f0a09ee996ab2605869844d6a3d812709e2bf0fa
任务模块、电话模块
已删除3个文件
已重命名103个文件
已修改106个文件
已添加35个文件
9391 ■■■■■ 文件已修改
ServiceTaskHandler.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/pom.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/IvrTaskVisitResultController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatArchiveController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatArchivecontactController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatArchivetagController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatTaskRelevanceController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/QRCodeController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceOutPathController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServicePhoneController.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailTraceController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskPreachformController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskRecordController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskRuleController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskdeptController.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskdiagController.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskmedicalController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskoperController.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskscheduleController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTelInfoController.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SvyLibTemplateScriptController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SvyTaskController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SvyTaskSingleController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/controller/sso/SSOController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/task/PhoneTask.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/java/com/ruoyi/web/test/MQTest.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationVisitTaskService.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/pom.xml 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/PhoneModule.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/api/PhoneApi.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/api/PhoneFacade.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneAvailabilityInterceptor.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneProperties.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneSwitch.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneWebConfig.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/controller/PhoneCallbackController.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/controller/ShPhoneController.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallBackSHVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallBackVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallBackYQVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallRecordVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallReqYQVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallResultYQVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PullTaskVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/QuestionMessage.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/service/IPhoneService.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/main/java/com/ruoyi/phone/service/impl/PhoneServiceImpl.java 2056 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-phone/src/test/java/com/ruoyi/phone/config/PhoneSwitchCheck.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/pom.xml 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/CollectHISMapper.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/domain/row/SatisfactionTaskRow.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/mapper/SatisfactionMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/service/SatisfactionSubtaskService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/service/impl/SatisfactionSubtaskServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/pom.xml 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/TaskModule.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskApi.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskFacade.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskSubtaskApi.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/ServiceTaskMatchApiImpl.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/SubtaskOptionMatchApiImpl.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/SubtaskStatApiImpl.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/TaskSubtaskApiImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/VisitTaskCreateApiImpl.java 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskArchivePort.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskFtpPort.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskPreachformPort.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSubtaskRecordPort.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSysConfigPort.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSysDeptPort.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSysUserDeptPort.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskAvailabilityInterceptor.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskProperties.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskSwitch.java 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskWebConfig.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceTaskController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/DTO/DetailTraceDealDTO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/DTO/ServiceSubtaskDetailDTO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/IvrTaskVisitResult.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/PatArchiveSrmVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/PatTaskRelevance.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/Question.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/SendMagParam.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceOutPath.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceStatisticsRequest.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceStatisticsResponse.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubTaskDetailReq.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCount.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetail.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetailTarget.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetailTrace.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetailVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDiagname.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskIsAbnormalStatistic.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskPreachform.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRecord.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskScriptCount.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskTemplateVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskRule.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskSchedule.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskScheduleDetail.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskScheduleVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskScriptQues.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskdept.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskdiag.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskmedical.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskoper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/TaskSendTimeVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/DetailTraceDealVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskCotinueCountVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskDetailTraceVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceTaskmedicalVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/TransferDeptVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/IvrTaskVisitResultMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceOutPathMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskDetailMapper.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskDetailTraceMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskRuleMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskScheduleDetailMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskScheduleMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskdeptMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskdiagMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskmedicalMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskoperMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceOutPathService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskDetailService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskDetailTraceService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskScheduleService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdiagService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskmedicalService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskoperService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceOutPathServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskDetailServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskDetailTraceServiceImpl.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java 2109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdiagServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskmedicalServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskoperServiceImpl.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/IvrTaskVisitResultMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceOutPathMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskDetailMapper.xml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskDetailTraceMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskRuleMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskScheduleDetailMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskScheduleMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskdiagMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskmedicalMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/main/resources/mapper/task/ServiceTaskoperMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-task/src/test/java/com/ruoyi/task/config/TaskSwitchCheck.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplatescriptService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplatescriptServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/pom.xml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/CommonTaskcallMQ.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/HeTaskSingleVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/HeTaskVO.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatArchive.java 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatArchiveOthreInfo.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatArchiveVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatArchivecontact.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatArchivetag.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatArchivetagAndPatientInfo.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/PatUpInfoVO.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceSubTaskAnswerReq.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/domain/SvyTaskVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/HNGatherPatArchiveMapper.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/PatArchiveMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/PatArchivecontactMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/PatArchivetagMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/PatTaskRelevanceMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskPreachformMapper.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskRecordMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/mapper/ShardingMapper.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IHNGatherPatArchiveService.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IIvrTaskVisitResultService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatArchiveService.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatArchivecontactService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatArchivetagService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatMedInhospService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IPatTaskRelevanceService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServiceExternalService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServicePhoneService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServiceSubtaskPreachformService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServiceSubtaskRecordService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/IServiceTaskRuleService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/ISvyLibTemplateScriptService.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/ISvyTaskSingleService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/CommonTaskServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/HNGatherPatArchiveServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/IvrTaskVisitResultServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatArchiveServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatArchivecontactServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatArchivetagServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatFilterlistServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/PatTaskRelevanceServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServicPhoneServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSLTDHealthcareRecordServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskAnswerServiceImpl.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskPreachformServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskRecordServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/ServiceTaskRuleServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/SubtaskSmsServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/SvyTaskServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/SvyTaskSingleServiceImpl.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/service/impl/XHGatherPatArchiveServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/java/com/smartor/statemachine/InhospStateMachineExample.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/HNGatherPatArchiveMapper.xml 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatArchiveMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatArchivecontactMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatArchivetagMapper.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/PatHetaskRelevanceMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
smartor/src/main/resources/mapper/smartor/ServiceSubtaskRecordMapper.xml 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ServiceTaskHandler.java
@@ -1,4 +1,5 @@
private boolean sendLishuiSms(ServiceSubtask serviceSubtask, SendMagParam sendMagParam) {
import com.ruoyi.task.domain.ServiceSubtask;
    Map<String, String> map = MsgLSEnum.getAddressByCode(serviceSubtask.getOrgid());
    String token = LSHospTokenUtil.getToken(serviceSubtask.getOrgid());
    if (ObjectUtils.isNotEmpty(map) && StringUtils.isEmpty(map.get("address"))) {
pom.xml
@@ -234,6 +234,20 @@
                <version>${ruoyi.version}</version>
            </dependency>
            <!-- ä»»åŠ¡æ¨¡å—ï¼ˆservice_task / service_subtask;只依赖 ruoyi-common,不依赖 smartor) -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-task</artifactId>
                <version>${ruoyi.version}</version>
            </dependency>
            <!-- ç”µè¯æ¨¡å—(上海/雨绮/三汇外呼回调与任务拉取;依赖 ruoyi-task å–任务数据,不依赖 smartor) -->
            <dependency>
                <groupId>com.ruoyi</groupId>
                <artifactId>ruoyi-phone</artifactId>
                <version>${ruoyi.version}</version>
            </dependency>
        </dependencies>
    </dependencyManagement>
@@ -252,7 +266,11 @@
        <module>ruoyi-outpatientvisit</module>
        <module>ruoyi-othervisit</module>
        <module>ruoyi-template</module>
        <!-- smartor ä¾èµ– ruoyi-satisfaction / ruoyi-inhospvisit / ruoyi-publicity / ruoyi-outpatientvisit / ruoyi-othervisit / ruoyi-template,必须排最后 -->
        <!-- ä»»åŠ¡æ¨¡å—ï¼šåªä¾èµ– ruoyi-common,必须排在依赖它的 smartor / ruoyi-quartz ä¹‹å‰ -->
        <module>ruoyi-task</module>
        <!-- ç”µè¯æ¨¡å—:依赖 ruoyi-task(任务数据)与 ruoyi-template(模板话术),必须排在 ruoyi-task ä¹‹åŽã€smartor ä¹‹å‰ -->
        <module>ruoyi-phone</module>
        <!-- smartor ä¾èµ– ruoyi-satisfaction / ruoyi-inhospvisit / ruoyi-publicity / ruoyi-outpatientvisit / ruoyi-othervisit / ruoyi-template / ruoyi-task / ruoyi-phone,必须排最后 -->
        <module>smartor</module>
    </modules>
    <packaging>pom</packaging>
ruoyi-admin/pom.xml
@@ -112,6 +112,26 @@
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-publicity</artifactId>
        </dependency>
        <!--
            ä»»åŠ¡æ¨¡å—ï¼ˆservice_task / service_subtask)。
            âš ï¸ è¿™é‡Œã€å¿…须显式声明】,不能只靠 smartor ä¼ é€’:
               æ»¡æ„åº¦é‚£æ¬¡å°±æ˜¯"æ ¹ pom æ³¨å†Œäº†æ¨¡å—,但没有任何模块直接依赖它",
               å¯¼è‡´æ¨¡å—根本没被打进 smartor-suifang.jar,新搬的端点全部不可达。
               æœ¬æ¨¡å—要放 Controller ä¸Ž Mapper XML,所以直接依赖。
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-task</artifactId>
        </dependency>
        <!--
            ç”µè¯æ¨¡å—(上海/雨绮/三汇外呼回调、任务拉取)。
            âš ï¸ åŒæ ·ã€å¿…须显式声明】:本模块要把 ruoyi-phone æ‰“è¿› smartor-suifang.jar,
               å¦åˆ™æ–°æ¬çš„电话端点(仍挂在 /smartor/serviceSubtask/** ä¸ŽåŽŸ /smartor/shphone/** ä¸Šï¼‰ä¸å¯è¾¾ã€‚
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-phone</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/net.glxn.qrgen/javase -->
        <dependency>
            <groupId>net.glxn.qrgen</groupId>
ruoyi-admin/src/main/java/com/ruoyi/web/component/RedisMqReceiver.java
@@ -18,10 +18,8 @@
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.IBaseSmsaccountService;
import com.smartor.service.IServiceOutPathService;
import com.smartor.service.IServiceSubtaskRecordService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -41,6 +39,17 @@
import com.ruoyi.template.domain.HeLibrary;
import com.ruoyi.template.api.TemplateFacade;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.mapper.ServiceTaskMapper;
import com.ruoyi.task.domain.ServiceOutPath;
import com.ruoyi.task.service.IServiceOutPathService;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.domain.SendMagParam;
@Slf4j
@Component//监听此队列
public class RedisMqReceiver extends KeyExpirationEventMessageListener {
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/SmsController.java
@@ -12,8 +12,6 @@
import com.ruoyi.message.wechat.WechatSendRequest;
import com.ruoyi.message.wechat.WechatSenderFactory;
import com.smartor.domain.*;
import com.smartor.service.IServiceOutPathService;
import com.smartor.service.IServiceSubtaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -27,6 +25,10 @@
import java.io.UnsupportedEncodingException;
import java.util.Date;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceOutPath;
import com.ruoyi.task.service.IServiceOutPathService;
import com.ruoyi.task.service.IServiceSubtaskService;
/**
 * çŸ¥ä¿¡æŽ¥å£ï¼ˆå¯¹å¤–短信 / å¾®ä¿¡å…¬ä¼—号发送入口)。
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/IvrTaskVisitResultController.java
@@ -7,7 +7,6 @@
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.IvrTaskVisitResult;
import com.smartor.service.IIvrTaskVisitResultService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -18,6 +17,7 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import com.ruoyi.task.domain.IvrTaskVisitResult;
/**
 * ä»»åŠ¡éšè®¿ç»“æžœController
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatArchiveController.java
@@ -31,6 +31,7 @@
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ‚£è€…档案Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatArchivecontactController.java
@@ -9,7 +9,6 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.PatArchivecontact;
import com.smartor.service.IPatArchivecontactService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -19,6 +18,7 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchivecontact;
/**
 * æ‚£è€…联系方式Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatArchivetagController.java
@@ -5,7 +5,6 @@
import com.ruoyi.common.annotation.AddOrgId;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.smartor.domain.PatArchivetag;
import com.smartor.service.IPatArchivetagService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -23,6 +22,7 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…档案标签Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/PatTaskRelevanceController.java
@@ -6,8 +6,6 @@
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.PatTaskRelevance;
import com.smartor.domain.PatTaskRelevance;
import com.smartor.service.IPatTaskRelevanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -15,6 +13,7 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * æ‚£è€…与宣教任务关联Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/QRCodeController.java
@@ -10,7 +10,6 @@
import com.mchange.v2.uid.UidUtils;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.framework.config.ServerConfig;
import com.smartor.domain.Question;
import com.ruoyi.template.domain.SvyLibTemplate;import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
@@ -34,6 +33,7 @@
import java.io.StringWriter;
import java.net.URI;
import java.util.*;
import com.ruoyi.task.domain.Question;
@Slf4j
@Api(description = "问卷二维码")
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceExternalController.java
@@ -9,7 +9,6 @@
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.domain.*;
import com.smartor.service.IServiceExternalService;
import com.smartor.service.IServiceSubtaskDetailService;
import com.smartor.service.ISvyTaskSingleService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceOutPathController.java
@@ -11,13 +11,8 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.BaseOrganization;
import com.smartor.domain.PatArchive;
import com.smartor.domain.ServiceOutPath;
import com.smartor.domain.ServiceTask;
import com.smartor.service.IBaseOrganizationService;
import com.smartor.service.IPatArchiveService;
import com.smartor.service.IServiceOutPathService;
import com.smartor.service.IServiceTaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
@@ -29,6 +24,10 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.ServiceOutPath;
import com.ruoyi.task.service.IServiceOutPathService;
import com.ruoyi.task.service.IServiceTaskService;
/**
 * ä¸»é”®Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServicePhoneController.java
@@ -3,11 +3,7 @@
import com.ruoyi.common.core.controller.BaseController;
import com.smartor.domain.OutboundConfigAddReqVO;
import com.smartor.domain.OutboundInstanceResVO;
import com.smartor.domain.PhoneCallBackSHVO;
import com.smartor.domain.PhoneCallReqYQVO;
import com.smartor.domain.PhoneCallResultYQVO;
import com.smartor.service.IServicePhoneService;
import com.smartor.service.IServiceSubtaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -21,6 +17,13 @@
/**
 * ä¸‰æ±‡ç”µè¯æµç¨‹æœåŠ¡ Controller
 * <p>
 * âš ï¸ è¯¥å‰ç¼€ä¸‹çš„<b>电话流程端点</b>({@code /smartor/shphone/getFirstQues}、
 * {@code /smartor/shphone/processResust})已随电话模块化搬进 {@code ruoyi-phone}
 * çš„ {@code ShPhoneController}(URL ä¿æŒä¸å˜ï¼Œä¸‰æ±‡å¹³å°æ— éœ€æ”¹å›žè°ƒåœ°å€ï¼‰ã€‚
 * <p>
 * è¿™é‡Œåªç•™ {@code /smartor/shphone/addOutboundInstance}:它依赖 smartor ä¾§çš„外呼实例配置
 * ï¼ˆ{@code IServicePhoneService}),属于"外呼实例开通"而非电话会话链路,不归电话模块。
 *
 * @author ls
 * @date 2026-08-26
@@ -30,27 +33,9 @@
@RestController
@RequestMapping("/smartor/shphone")
public class ServicePhoneController extends BaseController {
    @Autowired
    private IServiceSubtaskService serviceSubtaskService;
    @Autowired
    private IServicePhoneService iServicePhoneService;
    @ApiOperation("根据电话号码,获取首个问题")
    @PostMapping("/getFirstQues")
    public Map<String, Object> getFirstQues(@RequestBody PhoneCallReqYQVO phoneCallReqYQVO) {
        log.info("根据电话号码,获取首个问题?{}", phoneCallReqYQVO);
        PhoneCallBackSHVO phoneCallBackSHVO = serviceSubtaskService.getFirstQues(phoneCallReqYQVO);
        return success(phoneCallBackSHVO);
    }
    @ApiOperation("三汇 ASR ç»“果处理")
    @PostMapping("/processResust")
    public Map<String, Object> processResust(@RequestBody PhoneCallResultYQVO resultYQVO) {
        log.info("三汇 ASR ç»“果处理 (三汇)?{}", resultYQVO);
        PhoneCallBackSHVO phoneCallBackSHVO = serviceSubtaskService.processResust(resultYQVO);
        return success(phoneCallBackSHVO);
    }
    @ApiOperation("新增外呼实例配置")
    @PostMapping("/addOutboundInstance")
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailController.java
@@ -11,9 +11,6 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskDetailService;
import com.smartor.service.IServiceSubtaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
@@ -26,6 +23,12 @@
import java.util.stream.Collectors;
import static com.ruoyi.framework.datasource.DynamicDataSourceContextHolder.log;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailVO;
import com.ruoyi.task.service.IServiceSubtaskDetailService;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
/**
 * è¯­éŸ³ä»»åŠ¡ç»“æžœæ˜Žç»†Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskDetailTraceController.java
@@ -7,10 +7,10 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.ServiceSubtaskDetailTrace;
import com.smartor.domain.VO.DetailTraceDealVO;
import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.service.IServiceSubtaskDetailTraceService;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.DetailTraceDealVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -21,6 +21,10 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.DetailTraceDealVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
/**
 * éšè®¿ä»»åŠ¡å¼‚å¸¸è®°å½•è·Ÿè¸ªController
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskPreachformController.java
@@ -21,10 +21,10 @@
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.smartor.domain.ServiceSubtaskPreachform;
import com.smartor.service.IServiceSubtaskPreachformService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskRecordController.java
@@ -7,7 +7,6 @@
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.ServiceSubtaskRecord;
import com.smartor.service.IServiceSubtaskRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -15,6 +14,7 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskRuleController.java
@@ -25,10 +25,10 @@
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.smartor.domain.ServiceTaskRule;
import com.smartor.service.IServiceTaskRuleService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.task.domain.ServiceTaskRule;
/**
 * ä»»åŠ¡æ‰§è¡Œè§„åˆ™Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskdeptController.java
@@ -6,14 +6,14 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.smartor.domain.ServiceTaskdept;
import com.smartor.service.IServiceTaskdeptService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskdept;
import com.ruoyi.task.service.IServiceTaskdeptService;
/**
 * æ¨¡æ¿ç§‘室关联Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskdiagController.java
@@ -24,10 +24,10 @@
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.smartor.domain.ServiceTaskdiag;
import com.smartor.service.IServiceTaskdiagService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.task.domain.ServiceTaskdiag;
import com.ruoyi.task.service.IServiceTaskdiagService;
/**
 * æ¨¡æ¿ç§‘室关联Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskmedicalController.java
@@ -7,9 +7,6 @@
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.ServiceTaskmedical;
import com.smartor.domain.VO.ServiceTaskmedicalVO;
import com.smartor.service.IServiceTaskmedicalService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
@@ -17,6 +14,9 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskmedical;
import com.ruoyi.task.domain.VO.ServiceTaskmedicalVO;
import com.ruoyi.task.service.IServiceTaskmedicalService;
/**
 * ä»»åŠ¡è¯å“å…³è”Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskoperController.java
@@ -22,10 +22,10 @@
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.smartor.domain.ServiceTaskoper;
import com.smartor.service.IServiceTaskoperService;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.task.domain.ServiceTaskoper;
import com.ruoyi.task.service.IServiceTaskoperService;
/**
 * æ¨¡æ¿ç§‘室关联Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskscheduleController.java
@@ -11,11 +11,7 @@
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.system.service.ISysDeptService;
import com.smartor.domain.Icd10;
import com.smartor.domain.ServiceTaskSchedule;
import com.smartor.domain.ServiceTaskdiag;
import com.smartor.service.IIcd10Service;
import com.smartor.service.IServiceTaskScheduleService;
import com.smartor.service.IServiceTaskdiagService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
@@ -30,6 +26,8 @@
import java.util.Map;
import static com.alibaba.fastjson2.JSONValidator.Type.Array;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.service.IServiceTaskScheduleService;
/**
 * éšè®¿ä»»åŠ¡ç»„å…³è”Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTelInfoController.java
@@ -2,7 +2,6 @@
import com.ruoyi.common.core.domain.AjaxResult;
import com.smartor.domain.smsVO;
import com.smartor.service.IServiceTaskoperService;
import com.smartor.service.IServiceTelInfoService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SvyLibTemplateScriptController.java
@@ -9,7 +9,6 @@
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.ServiceSubtaskCountReq;
import com.ruoyi.template.domain.SvyLibTemplateScript;
import com.smartor.service.ISvyLibTemplateScriptService;
import io.swagger.annotations.ApiOperation;
@@ -21,6 +20,7 @@
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
/**
 * æ¨¡æ¿é—®å·é¢˜ç›®Controller
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SvyTaskController.java
@@ -13,8 +13,6 @@
import com.ruoyi.common.utils.RSAPublicKeyExample;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskService;
import com.smartor.service.ISvyTaskService;
import com.smartor.service.ISvyTaskSingleService;
import io.swagger.annotations.Api;
@@ -31,6 +29,10 @@
import java.util.stream.Collectors;
import com.smartor.domain.SvyTask;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.ruoyi.task.domain.ServiceTaskScriptQues;
/**
 * é—®å·ä»»åŠ¡ï¼ˆä»»åŠ¡ï¼‰Controller
 *
ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/SvyTaskSingleController.java
@@ -16,6 +16,8 @@
import com.smartor.domain.SvyTaskSingleVO;
import com.smartor.domain.SvyTaskVO;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTaskVO;
/**
 * å•一任务(问卷)Controller
 *
ruoyi-admin/src/main/java/com/ruoyi/web/controller/sso/SSOController.java
@@ -16,11 +16,6 @@
import com.ruoyi.system.service.ISysUserService;
import com.smartor.domain.SSOTokenResponse;
import com.smartor.domain.SSOUserInfo;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.VO.ServiceSubtaskVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskService;
import com.smartor.service.impl.ServiceSubtaskServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -41,6 +36,7 @@
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;
import com.ruoyi.task.service.IServiceSubtaskService;
/**
 * SSO单点登录控制器
ruoyi-admin/src/main/java/com/ruoyi/web/task/PhoneTask.java
@@ -18,6 +18,9 @@
import com.ruoyi.template.domain.IvrTaskTemplateScriptVO;
import com.ruoyi.template.domain.IvrTaskTemplateVO;
import com.ruoyi.task.domain.ServiceSubtask;
// ç”µè¯ä¼šè¯å¯¹è±¡å·²éšç”µè¯æ¨¡å—化搬进 ruoyi-phone(Redis é‡Œç¼“存的仍是这个类,供 PhoneServiceImpl æ¶ˆè´¹ï¼‰
import com.ruoyi.phone.domain.QuestionMessage;
public class PhoneTask implements Runnable {
    private ServiceSubtask ivrTaskSingle;
    private List<IvrTaskTemplateScriptVO> ivrTaskTemplateScriptVO;
ruoyi-admin/src/main/java/com/ruoyi/web/test/MQTest.java
@@ -7,7 +7,6 @@
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.smartor.domain.ServiceSubtaskDetail;
import com.smartor.domain.ThiedInhospInfo;
import com.smartor.service.IHNGatherPatArchiveService;
import io.swagger.models.auth.In;
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationVisitTaskService.java
@@ -20,6 +20,7 @@
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ‰‹æœ¯éšè®¿ç¼–排:扫描未处理的手术记录 â†’ æŒ‰<b>手术编码</b>匹配长期任务 â†’ å†™å­ä»»åŠ¡ã€‚
ruoyi-phone/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>3.8.5</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-phone</artifactId>
    <description>
        ç”µè¯æ¨¡å—:上海(phoneCallBack)/ é›¨ç»®ï¼ˆphoneCallBackYQ、taskPull、recordAccept)/ ä¸‰æ±‡ï¼ˆgetFirstQues、
        processResust)三条外呼通道的全部端点与业务实现的唯一归属处。
        æœ¬æ¨¡å—【不自带 Mapper】:service_subtask / service_subtask_detail / service_task ç­‰è¡¨å½’ ruoyi-task,
        æŒ‰ã€Œè·¨æ¨¡å—数据访问原则」(doc/跨模块数据访问原则与收口方案.md)写操作只能由归属模块执行,
        æ•…电话侧一律经 ruoyi-task çš„ com.ruoyi.task.api.TaskSubtaskApi å–数与落库;
        æ¨¡æ¿è¯æœ¯ï¼ˆivr_task_template* / ivr_liba_extemplatescript / svy_lib_script_category)经
        ruoyi-template çš„ service æŽ¥å£è¯»å–。
        å…¶å®ƒæ¨¡å—需要电话能力只能通过 com.ruoyi.phone.api.PhoneFacade(模块门面)调用。
        æ”¯æŒé€šè¿‡ sys_config å‚数表里的 phone.enabled æ•´ä½“关闭(运行时读取,改完最多 10 ç§’生效)。
        ä¾èµ–方向:ruoyi-phone -> ruoyi-task / ruoyi-template / ruoyi-common
        ï¼ˆç»ä¸ä¾èµ– smartor / ruoyi-system / ruoyi-framework / ruoyi-admin,否则形成循环依赖,Maven ç›´æŽ¥æž„建失败)。
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·ï¼šBaseController / RedisCache / StringUtils / PhoneUtils / HttpUtil / RSAPublicKeyExample
             åŒæ—¶ä¼ é€’带入 pagehelper(含 mybatis)、swagger-annotations、commons-lang3、jackson ç­‰ -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <!--
            ä»»åŠ¡æ¨¡å—ï¼šç”µè¯æµç¨‹çš„"数据面"全在这里 â€”—
              Â· com.ruoyi.task.api.TaskSubtaskApi      å­ä»»åŠ¡/明细/访问结果/异常跟踪的读写
              Â· com.ruoyi.task.api.spi.TaskPreachformPort / TaskFtpPort / TaskSysConfigPort
              Â· com.ruoyi.task.domain.*                å­ä»»åŠ¡ã€æ˜Žç»†ã€ä»»åŠ¡æ¨¡æ¿å¼•ç”¨ç­‰ç±»åž‹
            ï¼ˆä»»åŠ¡è¡¨å½’ ruoyi-task,本模块不写任何针对这些表的 SQL。)
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-task</artifactId>
        </dependency>
        <!--
            æ¨¡æ¿æ¨¡å—:问题话术 / é€‰é¡¹ / é€šç”¨åº“ / é—®é¢˜åˆ†ç±»çš„读取与"用户已作答"标记回写。
            ruoyi-template åªä¾èµ– ruoyi-common,不会与 ruoyi-phone å½¢æˆå¾ªçŽ¯ä¾èµ–ã€‚
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-template</artifactId>
        </dependency>
        <!-- lombok:ruoyi-common é‡Œæ˜¯ç›´æŽ¥ä¾èµ–,此处显式声明版本,避免注解处理器缺失 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.34</version>
        </dependency>
        <!-- Servlet API:HandlerInterceptor(模块开关拦截器用) -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
ruoyi-phone/src/main/java/com/ruoyi/phone/PhoneModule.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package com.ruoyi.phone;
/**
 * ç”µè¯æ¨¡å—包根。
 *
 * <h3>这个模块装了什么</h3>
 * åŽŸå…ˆæ•£åœ¨ {@code ruoyi-task} çš„ {@code ServiceSubtaskController} ä¸Ž
 * {@code ServiceSubtaskServiceImpl} é‡Œçš„"电话"能力,本模块化后<b>整体搬到这里</b>:
 * <ul>
 *   <li>{@code controller} â€”— ä¸‰æ¡å¤–呼通道的回调端点,<b>URL ä¸ŽåŽŸæ ·å®Œå…¨ä¸€è‡´</b>
 *       ï¼ˆä¸Šæµ·/雨绮挂在 {@code /smartor/serviceSubtask/**},三汇挂在 {@code /smartor/shphone/**}),
 *       å¤–部电话平台不需要改回调地址;</li>
 *   <li>{@code service} â€”— ç”µè¯ä¼šè¯çŠ¶æ€æœºï¼ˆredis ä¼šè¯ã€TTS æ’­æŠ¥ã€ASR åŒ¹é…ã€AI åŒ¹é…ã€é™é»˜/挂机、
 *       é€šè¯è®°å½•落库)的全部实现;</li>
 *   <li>{@code domain} â€”— ç”µè¯å›žè°ƒ/拉取的入参与出参;</li>
 *   <li>{@code api} â€”— <b>唯一对外包</b>:其它模块只能经 {@code PhoneFacade} è°ƒç”µè¯èƒ½åŠ›ã€‚</li>
 * </ul>
 *
 * <h3>数据访问边界(重要)</h3>
 * {@code service_subtask} / {@code service_subtask_detail} / {@code service_task} ç­‰è¡¨å½’ {@code ruoyi-task},
 * æŒ‰ {@code doc/跨模块数据访问原则与收口方案.md} çš„落地口径,<b>非归属模块一律禁止写这些表</b>;
 * å› æ­¤æœ¬æ¨¡å—<b>刻意不带 Mapper / XML</b>:
 * <ul>
 *   <li>任务数据 â†’ {@code com.ruoyi.task.api.TaskSubtaskApi}(归属模块内实现,本模块只调 API);</li>
 *   <li>模板话术 â†’ {@code com.ruoyi.template.service} ä¸‹çš„ service æŽ¥å£ï¼›</li>
 *   <li>发送方式状态 â†’ {@code com.ruoyi.task.api.spi.TaskPreachformPort};</li>
 *   <li>录音下载 â†’ {@code com.ruoyi.task.api.spi.TaskFtpPort};</li>
 *   <li>参数配置 â†’ {@code com.ruoyi.task.api.spi.TaskSysConfigPort};</li>
 *   <li>AI é€‰é¡¹åŒ¹é… â†’ {@code com.ruoyi.common.core.service.SubtaskOptionMatchApi}(ruoyi-common å¥‘约)。</li>
 * </ul>
 *
 * <h3>开关</h3>
 * æ¨¡å—可通过 {@code sys_config} å‚数表的 {@code phone.enabled=false} æ•´ä½“关闭
 * ï¼ˆè¿è¡Œæ—¶è¯»å–,改完最多 10 ç§’生效):命中的端点被 {@code PhoneAvailabilityInterceptor}
 * æ‹¦ä¸‹å¹¶è¿”回明确的"未启用"业务错误;门面 {@code PhoneFacade} ä»å¸¸é©»ï¼Œè°ƒç”¨é™çº§ä¸ºç©ºå€¼/空列表。
 */
public final class PhoneModule {
    /**
     * æ¨¡å—配置前缀(仅作为 yml å…œåº•配置的键前缀;主开关在 sys_config å‚数表里)
     */
    public static final String PROPERTIES_PREFIX = "phone";
    private PhoneModule() {
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/api/PhoneApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package com.ruoyi.phone.api;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallBackVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PhoneCallRecordVO;
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
import com.ruoyi.phone.domain.PhoneCallResultYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
import java.util.List;
/**
 * ç”µè¯æ¨¡å—对外契约(<b>唯一对外包 {@code com.ruoyi.phone.api} çš„入口</b>)。
 *
 * <h3>谁该用它</h3>
 * å…¶å®ƒæ¨¡å—(随访编排、定时任务、AI æœºå™¨äººå›žè°ƒç­‰ï¼‰è¦"用到电话的功能"时,
 * æ³¨å…¥ {@link PhoneFacade}(常驻 Bean)调这里的方法;<b>不要</b>去注入本模块的
 * {@code IPhoneService} å®žçŽ°ã€ä¹Ÿä¸è¦ import æœ¬æ¨¡å—çš„ controller/service å†…部包。
 *
 * <h3>方法语义(与外部电话平台的回调一一对应,行为与原 {@code IServiceSubtaskService} å®Œå…¨ä¸€è‡´ï¼‰</h3>
 * <ul>
 *   <li>{@link #phoneCallBack} â€”— ä¸Šæµ·é€šé“:语音识别结果上报,驱动多轮问答与挂机;</li>
 *   <li>{@link #phoneCallBackYQ} â€”— é›¨ç»®é€šé“:ASR / é™é»˜ / æ”¾éŸ³äº‹ä»¶å›žè°ƒï¼Œè¿”回下一步播报内容;</li>
 *   <li>{@link #taskPull} â€”— é›¨ç»®é€šé“:按小时配额拉取待外呼子任务(含开场白与首题);</li>
 *   <li>{@link #saveQuestionAnswerPhone} â€”— ç”µè¯ä¾§æ‚£è€…作答落库(走子任务明细);</li>
 *   <li>{@link #recordAccept} â€”— é›¨ç»®é€šé“:通话记录回调(无人接听/已完成/拒访);</li>
 *   <li>{@link #getFirstQues} / {@link #processResust} â€”— ä¸‰æ±‡é€šé“:取首题、ASR ç»“果处理。</li>
 * </ul>
 *
 * <h3>关闭时({@code phone.enabled=false})的行为</h3>
 * {@link PhoneFacade} ä»å¯æ³¨å…¥ï¼Œä½†æ‰€æœ‰æ–¹æ³•<b>降级为空值/空列表并打 WARN,不抛异常</b>,
 * ä¿è¯è°ƒç”¨æ–¹ä¸»æµç¨‹ä¸è¢«æ‹–垮(与满意度门面同一约束)。
 */
public interface PhoneApi {
    /**
     * ç”µè¯æ¨¡å—是否启用(运行时读 {@code sys_config: phone.enabled},最多 10 ç§’生效)
     */
    boolean isEnabled();
    /**
     * ç”µè¯å›žè°ƒï¼ˆä¸Šæµ·ï¼‰ï¼šè¯­éŸ³è¯†åˆ«ç»“果上报
     *
     * @param phoneCallBackVO å›žè°ƒå…¥å‚(uuid + è¯†åˆ«æ–‡æœ¬ï¼‰
     */
    void phoneCallBack(PhoneCallBackVO phoneCallBackVO);
    /**
     * ç”µè¯ ASR é€šè¯å›žè°ƒï¼ˆé›¨ç»®ï¼‰
     *
     * @param phoneCallReqYQVO å›žè°ƒå…¥å‚(taskid å³å­ä»»åŠ¡ ID + å½“前题目的识别文本)
     * @return ä¸‹ä¸€æ­¥è¦æ’­æŠ¥çš„内容(type/value/silent_interval);无可播报内容时返回空对象
     */
    PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO);
    /**
     * ç”µè¯ä»»åŠ¡æ‹‰å–ï¼ˆé›¨ç»®ï¼‰
     *
     * @return å¾…外呼任务列表;无可拉取数据时返回空列表
     */
    List<PullTaskVO> taskPull();
    /**
     * æ‚£è€…问题结果记录(电话)
     *
     * @param serviceSubTaskDetailReq æ˜Žç»†é›†åˆï¼ˆå…¥å‚为子任务 ID æˆ– taskid+patid åŠ å¯†ä¸²ï¼‰
     * @return è½åº“条数;模块关闭时返回 0
     */
    Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq);
    /**
     * é€šè¯è®°å½•回调(雨绮)
     *
     * @param phoneCallRecordVO é€šè¯è®°å½•(挂断原因、录音路径等)
     * @return å¤„理结果(固定 1,保持原实现语义)
     */
    Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO);
    /**
     * æ ¹æ®ç”µè¯å·ç èŽ·å–é¦–ä¸ªé—®é¢˜ï¼ˆä¸‰æ±‡ï¼‰
     *
     * @param phoneCallReqYQVO å…¥å‚(phone)
     * @return å­ä»»åŠ¡ ID + é¦–题题号/内容;查不到时返回仅含默认 status çš„空对象
     */
    PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO);
    /**
     * ä¸‰æ±‡ ASR ç»“果处理
     *
     * @param phoneCallResultYQVO ASR ç»“果(subId、scriptId、识别文本)
     * @return ä¸‹ä¸€é¢˜å†…容;匹配失败且重问次数用尽时返回题目切换结果
     */
    PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO);
}
ruoyi-phone/src/main/java/com/ruoyi/phone/api/PhoneFacade.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,170 @@
package com.ruoyi.phone.api;
import com.ruoyi.phone.config.PhoneProperties;
import com.ruoyi.phone.config.PhoneSwitch;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallBackVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PhoneCallRecordVO;
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
import com.ruoyi.phone.domain.PhoneCallResultYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
import com.ruoyi.phone.service.IPhoneService;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
/**
 * ç”µè¯æ¨¡å—对外门面(<b>常驻 Bean</b>)。
 *
 * <h3>为什么必须"常驻"</h3>
 * æœ¬é—¨é¢<b>永远</b>是 Spring Bean,不随开关状态增删。这样调用方的
 * {@code @Autowired PhoneApi} åœ¨ä»»ä½•开关状态下都<b>不会启动失败</b>
 * â€”— ä¸Ž {@code TaskFacade} / {@code SatisfactionFacade} æ˜¯åŒä¸€æ¡è®¾è®¡çº¦æŸ
 * ï¼ˆåŽ†å²æ•™è®­ï¼šç”¨ {@code @ConditionalOnProperty} æ‘˜ Bean ä¼šè®©è°ƒç”¨æ–¹å¯åŠ¨å³å¤±è´¥ï¼Œ
 * ä¸”只在启动时求值一次,做不到"改完参数 10 ç§’生效")。
 *
 * <h3>关闭时的行为</h3>
 * å¼€å…³ï¼ˆ{@code sys_config: phone.enabled})关闭时,本门面各方法<b>返回空值/空列表并打 WARN</b>,
 * <b>不抛异常</b>:电话是随访的一条发送通道,模块被关掉时不能让调用方(随访编排、定时任务)
 * è·Ÿç€ç‚¸ã€‚HTTP ä¾§å¦æœ‰ {@code PhoneAvailabilityInterceptor} è´Ÿè´£"明确报错",
 * ä¸¤è€…分工:<b>端点 â†’ æ˜Žç¡®æŠ¥é”™ï¼›æ¨¡å—内调用 â†’ é™é»˜é™çº§</b>。
 *
 * <h3>使用示例</h3>
 * <pre>
 * &#64;Autowired
 * private PhoneApi phoneApi;
 *
 * if (phoneApi.isEnabled()) {
 *     List&lt;PullTaskVO&gt; tasks = phoneApi.taskPull();
 * }
 * </pre>
 */
@Slf4j
@Component
public class PhoneFacade implements PhoneApi {
    /**
     * è¿è¡Œæ—¶å¼€å…³ï¼ˆ{@code sys_config} ä¼˜å…ˆï¼Œ{@link PhoneProperties} å…œåº•)
     */
    private final PhoneSwitch phoneSwitch;
    /**
     * æ¨¡å—内实现(唯一实现 {@code PhoneServiceImpl})。
     * <p>
     * ä¸Žæ»¡æ„åº¦é—¨é¢ä¸åŒï¼Œè¿™é‡Œä¸éœ€è¦ {@code ObjectProvider} + è‡ªå¼•用过滤:
     * æœ¬ç±»<b>不实现</b> {@code IPhoneService},不存在"Spring æŠŠè‡ªå¼•用当兜底候选"导致的无限递归;
     * å®žçް Bean ä¹Ÿä¸å—任何条件装配控制,恒定存在。
     * <p>
     * å…è®¸ä¸º {@code null}:仅供测试/非 Spring çŽ¯å¢ƒæž„é€ æ—¶ä½¿ç”¨ï¼ˆæ­¤æ—¶æ‰€æœ‰æ–¹æ³•æŒ‰"不可用"降级)。
     */
    private final IPhoneService phoneService;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>显式标注 {@code @Autowired}</b>:本类有两个公开构造器(另一个给测试用),
     * æ ‡æ³¨åŽ Spring ä¸ä¼šé€€å›žæŸ¥æ‰¾æ— å‚构造器。该规则已由
     * {@code sql/_probe/verify_spring_beans.py} é™æ€æŠŠå…³ã€‚
     */
    @Autowired
    public PhoneFacade(PhoneSwitch phoneSwitch, IPhoneService phoneService) {
        this.phoneSwitch = phoneSwitch;
        this.phoneService = phoneService;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造:{@code sysConfigValue} å°±æ˜¯ {@code sys_config}
     * é‡Œ {@code phone.enabled} çš„值,{@code null} è¡¨ç¤º"参数行不存在";{@code phoneService} å¯ä¸º {@code null}。
     */
    public PhoneFacade(PhoneProperties properties, String sysConfigValue, IPhoneService phoneService) {
        this(new PhoneSwitch(properties, sysConfigValue), phoneService);
    }
    /**
     * å–运行时开关(供拦截器复用同一实例、也供测试断言)
     */
    public PhoneSwitch getPhoneSwitch() {
        return phoneSwitch;
    }
    @Override
    public boolean isEnabled() {
        return phoneSwitch.isEnabled();
    }
    @Override
    public void phoneCallBack(PhoneCallBackVO phoneCallBackVO) {
        if (!available("phoneCallBack")) {
            return;
        }
        phoneService.phoneCallBack(phoneCallBackVO);
    }
    @Override
    public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO) {
        if (!available("phoneCallBackYQ")) {
            return new PhoneCallBackYQVO();
        }
        return phoneService.phoneCallBackYQ(phoneCallReqYQVO);
    }
    @Override
    public List<PullTaskVO> taskPull() {
        if (!available("taskPull")) {
            return Collections.emptyList();
        }
        return phoneService.taskPull();
    }
    @Override
    public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
        if (!available("saveQuestionAnswerPhone")) {
            return 0;
        }
        return phoneService.saveQuestionAnswerPhone(serviceSubTaskDetailReq);
    }
    @Override
    public Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO) {
        if (!available("recordAccept")) {
            return 0;
        }
        return phoneService.recordAccept(phoneCallRecordVO);
    }
    @Override
    public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO) {
        if (!available("getFirstQues")) {
            return new PhoneCallBackSHVO();
        }
        return phoneService.getFirstQues(phoneCallReqYQVO);
    }
    @Override
    public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO) {
        if (!available("processResust")) {
            return new PhoneCallBackSHVO();
        }
        return phoneService.processResust(phoneCallResultYQVO);
    }
    /**
     * å¼€å…³ + å®žçŽ°å¯ç”¨æ€§åˆ¤æ–­ï¼Œä¸å¯ç”¨æ—¶æ‰“ WARN å¹¶è®©è°ƒç”¨æ–¹é™çº§
     */
    private boolean available(String method) {
        if (!phoneSwitch.isEnabled()) {
            log.warn("【电话】模块已关闭(参数配置 phone.enabled=false),{} æœ¬æ¬¡è°ƒç”¨é™çº§ä¸ºç©ºç»“æžœ", method);
            return false;
        }
        if (phoneService == null) {
            log.warn("【电话】未找到电话模块实现(IPhoneService ç¼ºå¤±ï¼‰ï¼Œ{} æœ¬æ¬¡è°ƒç”¨é™çº§ä¸ºç©ºç»“æžœ", method);
            return false;
        }
        return true;
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneAvailabilityInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.phone.config;
import com.ruoyi.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * ç”µè¯æ¨¡å—开关拦截器。
 * <p>
 * <b>作用</b>:当开关关闭({@code sys_config} ä¸­ {@code phone.enabled=false})时,
 * å‘½ä¸­ç”µè¯ç«¯ç‚¹çš„请求会被立即拦下并抛出 {@link ServiceException},
 * ç”± RuoYi çš„全局异常处理器返回<b>明确的业务错误提示</b>(而不是 404 æˆ–静默返回空数据)。
 * <p>
 * å¼€å…³å€¼ç”± {@link PhoneSwitch} åœ¨è¿è¡Œæ—¶è¯»å–({@code sys_config},10 ç§’进程内缓存),
 * å› æ­¤æ”¹å®Œå‚数设置<b>不需要重启</b>,最多 10 ç§’即生效。
 * <p>
 * <b>为什么不用 {@code @ConditionalOnProperty} è®© Controller ç›´æŽ¥ä¸æ³¨å†Œ</b>:
 * <ul>
 *   <li>那样调用方只会拿到 HTTP 404 â€”— è™½ç„¶ä¹Ÿæ˜¯å¤±è´¥ï¼Œä½†ä¸æ˜¯"明确错误",
 *       ç”µè¯å¹³å°æ— æ³•区分"模块被关闭"和"地址写错了";</li>
 *   <li>而且 {@code @ConditionalOnProperty} åªåœ¨<b>启动时</b>求值一次,
 *       åšä¸åˆ°"改完参数配置就生效",与参数表开关的诉求冲突。</li>
 * </ul>
 * <p>
 * <b>为什么不会影响任务/随访等其它接口</b>:本拦截器只登记在电话模块自己的 8 ä¸ªç²¾ç¡®è·¯å¾„上
 * ï¼ˆè§ {@link PhoneWebConfig}),不会碰到随访/问卷/统计接口。
 */
@Slf4j
@Component
public class PhoneAvailabilityInterceptor implements HandlerInterceptor {
    /**
     * æ¨¡å—被关闭时返回给调用方的错误提示(电话平台/前端可直接展示)
     */
    public static final String DISABLED_MESSAGE =
            "电话功能未启用(参数配置 phone.enabled=false),该接口不可用";
    private final PhoneSwitch phoneSwitch;
    public PhoneAvailabilityInterceptor(PhoneSwitch phoneSwitch) {
        this.phoneSwitch = phoneSwitch;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!phoneSwitch.isEnabled()) {
            // request å…è®¸ä¸º null(单元测试直接调 preHandle æ—¶ä¸æž„造请求对象)
            String method = request == null ? "-" : request.getMethod();
            String uri = request == null ? "-" : request.getRequestURI();
            log.warn("【电话】模块已关闭(参数配置 phone.enabled=false),拒绝访问 {} {}", method, uri);
            throw new ServiceException(DISABLED_MESSAGE);
        }
        return true;
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.phone.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * ç”µè¯æ¨¡å—配置(前缀 {@code phone})。
 * <p>
 * âš ï¸ <b>这不是主开关</b>。电话模块的开关<b>存在 {@code sys_config} å‚数配置表里</b>:
 * <pre>
 * å‚数键名:phone.enabled
 * å‚数键值:true(启用,默认)/ false(关闭)
 * </pre>
 * è§ {@link PhoneSwitch}。本类的 {@link #isEnabled()} åªä½œä¸º<b>兜底</b>:
 * å½“ {@code sys_config} é‡Œæ²¡æœ‰è¿™ä¸€è¡Œã€æˆ–非 Spring çŽ¯å¢ƒå–ä¸åˆ°é…ç½®æœåŠ¡æ—¶æ‰ä¼šç”¨åˆ°ã€‚
 * <p>
 * å¦‚需在 yml ä¸­å…œåº•(一般不必要,正常应在参数设置页维护):
 * <pre>
 * phone:
 *   enabled: false
 * </pre>
 */
@Data
@Component
@ConfigurationProperties(prefix = "phone")
public class PhoneProperties {
    /**
     * æ˜¯å¦å¯ç”¨ç”µè¯æ¨¡å—(默认启用)。
     * <p>
     * ä»…当 {@code sys_config} ä¸­æ²¡æœ‰ {@code phone.enabled} æ—¶ç”Ÿæ•ˆï¼Œè§ {@link PhoneSwitch}。
     */
    private boolean enabled = true;
}
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneSwitch.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,207 @@
package com.ruoyi.phone.config;
import com.ruoyi.common.core.service.IConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * ç”µè¯æ¨¡å—<b>总开关</b>:值存放在 {@code sys_config} å‚数配置表中,<b>运行时读取、可热切换</b>。
 *
 * <h3>参数配置</h3>
 * <pre>
 * å‚数键名:phone.enabled
 * å‚数键值:true  = å¯ç”¨ï¼ˆé»˜è®¤ï¼‰
 *          false = å…³é—­
 * </pre>
 * ä½ç½®ï¼šç³»ç»Ÿç®¡ç† â†’ å‚数设置 â†’ æ–°å¢ž/修改。
 * <pre>
 * insert into sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
 * values ('电话模块开关', 'phone.enabled', 'true', 'Y', 'admin', sysdate(), 'true启用,false关闭(默认true)');
 * </pre>
 *
 * <h3>取值优先级</h3>
 * <ol>
 *   <li>{@code sys_config} é‡Œ {@code phone.enabled} çš„值(<b>主用</b>,
 *       æ¥è‡ª {@code IConfigService} æŽ¥å£ï¼Œå®žçŽ°åœ¨ ruoyi-system çš„ {@code SysConfigServiceImpl});</li>
 *   <li>{@link PhoneProperties#isEnabled()}(<b>兜底</b>:仅当 sys_config é‡Œæ²¡æœ‰è¿™ä¸€è¡Œã€
 *       æˆ–非 Spring çŽ¯å¢ƒå–ä¸åˆ°é…ç½®æœåŠ¡æ—¶ä½¿ç”¨ï¼Œé»˜è®¤ true)。</li>
 * </ol>
 *
 * <h3>为什么默认 true</h3>
 * ä¸Žä»»åŠ¡/满意度模块同款:参数行缺失时按 true å¤„理,保证升级后电话功能不会因为少了一行配置而突然不可用
 * ï¼ˆè¦å…³é—­å¿…须显式配置 false)。
 *
 * <h3>为什么读不到配置时也按 true(fail-open)</h3>
 * è¯» {@code sys_config} éœ€è¦èµ°æ•°æ®åº“,数据库抖动时如果 fail-closed ä¼šæŠŠæ•´æ¡ç”µè¯é“¾è·¯æ‰“æ­»
 * ï¼ˆå›žè°ƒå¤±è´¥æ„å‘³ç€æ­£åœ¨é€šè¯çš„随访直接断掉)。这里只打 WARN å¹¶æ”¾è¡Œã€‚
 *
 * <h3>为什么有 10 ç§’缓存</h3>
 * {@code SysConfigServiceImpl#selectConfigByKey} å½“前每次都查库,而本开关在每次电话回调、
 * æ¯æ¬¡å®ˆå«åˆ¤æ–­æ—¶éƒ½ä¼šè¢«è¯»åˆ°ã€‚这里加一个进程内 TTL ç¼“存,把查库降到"每个实例每 10 ç§’最多一次"。
 * ä»£ä»·æ˜¯å‚数改动最多 10 ç§’生效(多实例部署时各自计时)。
 */
@Slf4j
@Component
public class PhoneSwitch {
    /**
     * sys_config é‡Œçš„参数键名
     */
    public static final String CONFIG_KEY = "phone.enabled";
    /**
     * è¿›ç¨‹å†…缓存有效期(毫秒)
     */
    private static final long CACHE_TTL_MILLIS = 10_000L;
    private final PhoneProperties phoneProperties;
    /**
     * å‚数配置服务(ruoyi-common çš„æŽ¥å£ï¼Œå®žçŽ°åœ¨ ruoyi-system)。
     * <p>
     * ç”¨ {@code ObjectProvider} æƒ°æ€§å–:本模块只依赖 ruoyi-common / ruoyi-task / ruoyi-template,
     * åœ¨å•元测试/独立部署等"容器里没有实现"的场景下不能因此启动失败。
     */
    private final ObjectProvider<IConfigService> configServiceProvider;
    /**
     * æœ€è¿‘一次解析结果
     */
    private volatile Boolean cachedEnabled;
    /**
     * æœ€è¿‘一次解析的时间戳
     */
    private volatile long cachedAt;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器
     * ï¼ˆå¦ä¸€ä¸ªç»™æµ‹è¯•/非 Spring çŽ¯å¢ƒç”¨ï¼‰ï¼Œä¸æ ‡æ³¨çš„è¯ Spring ä¼šå› "构造器不唯一"
     * é€€å›žæŸ¥æ‰¾æ— å‚构造器,启动即报 {@code No default constructor found}。
     * è¯¥è§„则已由 {@code sql/_probe/verify_spring_beans.py} é™æ€æŠŠå…³ã€‚
     */
    @Autowired
    public PhoneSwitch(PhoneProperties phoneProperties,
                       ObjectProvider<IConfigService> configServiceProvider) {
        this.phoneProperties = phoneProperties;
        this.configServiceProvider = configServiceProvider;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造:{@code configValue} å°±æ˜¯ sys_config é‡Œçš„键值,
     * ä¼  {@code null} è¡¨ç¤º"参数行不存在"(此时回落到 {@link PhoneProperties})。
     */
    public PhoneSwitch(PhoneProperties phoneProperties, final String configValue) {
        this(phoneProperties, new FixedProvider(configValue));
        // éž Spring çŽ¯å¢ƒä¸‹æ²¡æœ‰"改配置后等 10 ç§’"的概念,直接禁用缓存,保证断言即时可见
        this.cachedEnabled = resolve();
        this.cachedAt = System.currentTimeMillis() + CACHE_TTL_MILLIS * 1000L;
    }
    /**
     * ç”µè¯æ¨¡å—是否启用(运行时热读,最多 10 ç§’延迟)
     */
    public boolean isEnabled() {
        long now = System.currentTimeMillis();
        Boolean cached = this.cachedEnabled;
        if (cached != null && now - this.cachedAt < CACHE_TTL_MILLIS) {
            return cached;
        }
        boolean resolved = resolve();
        this.cachedEnabled = resolved;
        this.cachedAt = now;
        return resolved;
    }
    /**
     * æ¸…掉进程内缓存,下次调用立即重新读 sys_config(供测试/需要立即生效的场景)
     */
    public void invalidateCache() {
        this.cachedEnabled = null;
    }
    /**
     * è§£æžå½“前开关值:sys_config ä¼˜å…ˆï¼Œå…¶æ¬¡ yml å…œåº•
     */
    private boolean resolve() {
        String value = readFromSysConfig();
        if (value != null) {
            if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value)) {
                log.warn("【电话】sys_config ä¸­ {} çš„值不是 true/false(实际=「{}」),将按「不是 false å³å¯ç”¨ã€å¤„理",
                        CONFIG_KEY, value);
            }
            boolean enabled = !"false".equalsIgnoreCase(value);
            log.debug("【电话】开关取自 sys_config:{}={} -> enabled={}", CONFIG_KEY, value, enabled);
            return enabled;
        }
        boolean enabled = phoneProperties.isEnabled();
        log.debug("【电话】sys_config ä¸­æœªé…ç½® {},回落 phone.enabled={}", CONFIG_KEY, enabled);
        return enabled;
    }
    /**
     * è¯» sys_config çš„原始键值;没有实现、没有这一行、或读库失败都返回 {@code null}
     */
    private String readFromSysConfig() {
        if (configServiceProvider == null) {
            return null;
        }
        try {
            IConfigService configService = configServiceProvider.stream().findFirst().orElse(null);
            if (configService == null) {
                return null;
            }
            String value = configService.selectConfigByKey(CONFIG_KEY);
            if (value == null || value.trim().isEmpty()) {
                return null;
            }
            return value.trim();
        } catch (Exception e) {
            // å®¹å™¨æ²¡æœ‰å®žçŽ°ã€æ•°æ®æºå¼‚å¸¸ã€SQL æŠ¥é”™â€¦â€¦ä¸€å¾‹æŒ‰"参数缺失"处理(fail-open),绝不影响主流程
            log.warn("【电话】读取 sys_config çš„ {} å¤±è´¥ï¼Œæœ¬æ¬¡æŒ‰æœªé…ç½®å¤„理(fail-open):{}",
                    CONFIG_KEY, e.toString());
            return null;
        }
    }
    /**
     * å›ºå®šå€¼çš„ {@code ObjectProvider}:只用于非 Spring çŽ¯å¢ƒ/测试
     */
    private static final class FixedProvider implements ObjectProvider<IConfigService> {
        private final IConfigService configService;
        private FixedProvider(final String configValue) {
            this.configService = configValue == null ? null : configKey -> configValue;
        }
        @Override
        public IConfigService getObject(Object... args) {
            return configService;
        }
        @Override
        public IConfigService getIfAvailable() {
            return configService;
        }
        @Override
        public IConfigService getIfUnique() {
            return configService;
        }
        @Override
        public IConfigService getObject() {
            return configService;
        }
        @Override
        public java.util.stream.Stream<IConfigService> stream() {
            return configService == null ? java.util.stream.Stream.empty() : java.util.stream.Stream.of(configService);
        }
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/config/PhoneWebConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,133 @@
package com.ruoyi.phone.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
 * ç”µè¯æ¨¡å—çš„ MVC é…ç½®ï¼šæŠŠ {@link PhoneAvailabilityInterceptor} æŒ‚到电话模块自己的端点上。
 *
 * <h3>为什么必须逐个精确枚举(不能用前缀)</h3>
 * ç”µè¯ç«¯ç‚¹<b>刻意保持原有 URL ä¸å˜</b>(外部电话平台的回调地址不能改),因此它散落在两个前缀下:
 * <pre>
 * /smartor/serviceSubtask/phoneCallBack         ä¸Šæµ·ï¼šè¯­éŸ³è¯†åˆ«ç»“果回调
 * /smartor/serviceSubtask/phoneCallBackYQ       é›¨ç»®ï¼šASR / é™é»˜ / æ’­æ”¾äº‹ä»¶å›žè°ƒ
 * /smartor/serviceSubtask/taskPull              é›¨ç»®ï¼šä»»åŠ¡æ‹‰å–
 * /smartor/serviceSubtask/saveQuestionAnswerPhone  ç”µè¯ï¼šæ‚£è€…问题结果记录
 * /smartor/serviceSubtask/recordAcceptshanghai  ä¸Šæµ·ï¼šé€šè¯è®°å½•回调
 * /smartor/serviceSubtask/recordAccept          é›¨ç»®ï¼šé€šè¯è®°å½•回调
 * /smartor/shphone/getFirstQues                 ä¸‰æ±‡ï¼šæŒ‰å·ç å–首个问题
 * /smartor/shphone/processResust                ä¸‰æ±‡ï¼šASR ç»“果处理
 * </pre>
 * è¿™ä¸¤ä¸ªå‰ç¼€<b>都不是电话模块独占</b>:
 * <ul>
 *   <li>{@code /smartor/serviceSubtask/**} ä¸‹è¿˜æœ‰ 40+ ä¸ªéšè®¿ç«¯ç‚¹ï¼ˆå½’ ruoyi-task);</li>
 *   <li>{@code /smartor/shphone/**} ä¸‹è¿˜æœ‰ {@code addOutboundInstance}(归 smartor çš„外呼实例配置)。</li>
 * </ul>
 * ç”¨å‰ç¼€ä¼šæŠŠåˆ«äººçš„端点一起拦掉,所以这里只挂上面这 8 æ¡ç²¾ç¡®è·¯å¾„。
 * <p>
 * ä¸Žä¹‹é…å¥—:{@code TaskWebConfig} é‡ŒæŠŠå‰ 6 æ¡ï¼ˆç”µè¯è‡ªå·±çš„)用 {@code excludePathPatterns} æŽ’除掉,
 * é¿å…"关任务"顺手把电话回调也关了 â€”— ç”µè¯å·²ç‹¬ç«‹æˆæ¨¡å—,开关理应各自独立。
 *
 * <h3>新增端点时的约定</h3>
 * å¾€åŽæœ¬æ¨¡å—若再加端点:默认<b>不</b>加入本清单(回调类端点必须加),
 * å¹¶åŒæ­¥æ›´æ–° {@code PhoneSwitchCheck} é‡Œçš„受保护路径条数断言。
 */
@Configuration
public class PhoneWebConfig implements WebMvcConfigurer {
    /**
     * ä¸Šæµ·ï¼šè¯­éŸ³è¯†åˆ«ç»“果回调
     */
    public static final String PHONE_CALLBACK_ENDPOINT = "/smartor/serviceSubtask/phoneCallBack";
    /**
     * é›¨ç»®ï¼šASR / é™é»˜ / æ’­æ”¾äº‹ä»¶å›žè°ƒ
     */
    public static final String PHONE_CALLBACK_YQ_ENDPOINT = "/smartor/serviceSubtask/phoneCallBackYQ";
    /**
     * é›¨ç»®ï¼šä»»åŠ¡æ‹‰å–
     */
    public static final String TASK_PULL_ENDPOINT = "/smartor/serviceSubtask/taskPull";
    /**
     * ç”µè¯ï¼šæ‚£è€…问题结果记录
     */
    public static final String SAVE_QUESTION_ANSWER_PHONE_ENDPOINT =
            "/smartor/serviceSubtask/saveQuestionAnswerPhone";
    /**
     * ä¸Šæµ·ï¼šé€šè¯è®°å½•回调
     */
    public static final String RECORD_ACCEPT_SHANGHAI_ENDPOINT =
            "/smartor/serviceSubtask/recordAcceptshanghai";
    /**
     * é›¨ç»®ï¼šé€šè¯è®°å½•回调
     */
    public static final String RECORD_ACCEPT_ENDPOINT = "/smartor/serviceSubtask/recordAccept";
    /**
     * ä¸‰æ±‡ï¼šæŒ‰å·ç å–首个问题
     */
    public static final String SH_GET_FIRST_QUES_ENDPOINT = "/smartor/shphone/getFirstQues";
    /**
     * ä¸‰æ±‡ï¼šASR ç»“果处理
     */
    public static final String SH_PROCESS_RESUST_ENDPOINT = "/smartor/shphone/processResust";
    @Autowired
    private PhoneAvailabilityInterceptor phoneAvailabilityInterceptor;
    /**
     * ä¾›æµ‹è¯•/非 Spring çŽ¯å¢ƒç›´æŽ¥æž„é€ 
     */
    public PhoneWebConfig() {
    }
    public PhoneWebConfig(PhoneAvailabilityInterceptor phoneAvailabilityInterceptor) {
        this.phoneAvailabilityInterceptor = phoneAvailabilityInterceptor;
    }
    /**
     * æœ¬æ¨¡å—需要"关闭即明确报错"的 URL è·¯å¾„(供校验脚本/单元测试断言)
     */
    public static String[] guardedPathPatterns() {
        return new String[]{
                PHONE_CALLBACK_ENDPOINT,
                PHONE_CALLBACK_YQ_ENDPOINT,
                TASK_PULL_ENDPOINT,
                SAVE_QUESTION_ANSWER_PHONE_ENDPOINT,
                RECORD_ACCEPT_SHANGHAI_ENDPOINT,
                RECORD_ACCEPT_ENDPOINT,
                SH_GET_FIRST_QUES_ENDPOINT,
                SH_PROCESS_RESUST_ENDPOINT,
        };
    }
    /**
     * æŒ‚在 {@code /smartor/serviceSubtask/**} å‰ç¼€ä¸‹ã€ä½†å±žäºŽç”µè¯æ¨¡å—的路径
     * ï¼ˆä¾› {@code TaskWebConfig} æŽ’除,见类注释)。
     */
    public static String[] subtaskPathsOwnedByPhone() {
        return new String[]{
                PHONE_CALLBACK_ENDPOINT,
                PHONE_CALLBACK_YQ_ENDPOINT,
                TASK_PULL_ENDPOINT,
                SAVE_QUESTION_ANSWER_PHONE_ENDPOINT,
                RECORD_ACCEPT_SHANGHAI_ENDPOINT,
                RECORD_ACCEPT_ENDPOINT,
        };
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(phoneAvailabilityInterceptor)
                .addPathPatterns(guardedPathPatterns())
                // æ‹¦æˆªå™¨æœ¬èº«å¾ˆè½»ï¼ˆåªè¯»ä¸€ä¸ª boolean),order ç»™ä¸ªå°å€¼è®©å®ƒåœ¨ä¸šåŠ¡æ‹¦æˆªå™¨ä¹‹å‰æ‰§è¡Œ
                .order(-100);
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/controller/PhoneCallbackController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package com.ruoyi.phone.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.phone.api.PhoneApi;
import com.ruoyi.phone.domain.PhoneCallBackVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PhoneCallRecordVO;
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
 * å¤–呼回调 / ä»»åŠ¡æ‹‰å– Controller(上海 + é›¨ç»®ä¸¤æ¡é€šé“)。
 *
 * <h3>⚠️ URL ä¸Žæ¨¡å—化前完全一致</h3>
 * è¿™äº›ç«¯ç‚¹æ˜¯<b>外部电话平台</b>回调用地址,模块化时<b>刻意不改路径</b>
 * ï¼ˆ{@code /smartor/serviceSubtask/xxx}),否则线上需要同步改电话平台的配置,
 * ä¸” {@code SecurityConfig} çš„免登录白名单也要跟着改。
 * å› æ­¤æœ¬ Controller ä¸Ž {@code ruoyi-task} çš„ {@code ServiceSubtaskController}
 * <b>共用一个 URL å‰ç¼€</b>——只要完整路径不重复,Spring å¯ä»¥æ­£å¸¸æ³¨å†Œã€‚
 *
 * <h3>为什么走门面(PhoneApi)而不是内部 IPhoneService</h3>
 * ä¸Žå‡ºå…¥é™¢/满意度模块"模块只有门面一个入口"的约定一致:
 * é—¨é¢ç»Ÿä¸€æ‰¿æ‹…"开关关闭 â†’ é™çº§/明确报错"的处理,Controller ä¸å†å„写一遍判断。
 *
 * <h3>与模块开关的关系</h3>
 * è¿™ 8 ä¸ªç«¯ç‚¹ï¼ˆå«ä¸‰æ±‡é‚£ä¸ª Controller)由 {@code PhoneWebConfig} æŒ‚
 * {@code PhoneAvailabilityInterceptor};同时在 {@code TaskWebConfig} é‡ŒæŠŠå…¶ä¸­ 6 æ¡
 * ä»Ž"任务模块开关"的拦截范围里排除,做到两个模块的开关各管各的。
 *
 * @author ruoyi
 * @date 2024-08-08
 */
@Slf4j
@Api(description = "外呼回调(上海/雨绮)")
@RestController
@RequestMapping("/smartor/serviceSubtask")
public class PhoneCallbackController extends BaseController {
    @Autowired
    private PhoneApi phoneApi;
    /**
     * ç”µè¯å›žè°ƒä»»åŠ¡(上海)
     */
    @ApiOperation("电话回调任务")
    @PostMapping("/phoneCallBack")
    public AjaxResult phoneCallBack(@RequestBody PhoneCallBackVO phoneCallBackVO) {
        phoneApi.phoneCallBack(phoneCallBackVO);
        return success();
    }
    /**
     * ç”µè¯ASR通话回调(雨绮)
     */
    @ApiOperation("电话ASR通话回调(雨绮)")
    @PostMapping("/phoneCallBackYQ")
    public PhoneCallBackYQVO phoneCallBackYQ(@RequestBody PhoneCallReqYQVO phoneCallReqYQVO) {
        log.info("电话ASR通话回调(雨绮) ï¼Ÿ{}", phoneCallReqYQVO);
        return phoneApi.phoneCallBackYQ(phoneCallReqYQVO);
    }
    /**
     * ç”µè¯ä»»åŠ¡æ‹‰å–(雨绮)
     */
    @ApiOperation("电话任务拉取(雨绮)")
    @PostMapping("/taskPull")
    public List<PullTaskVO> taskPull() {
        log.info("电话任务拉取(雨绮)");
        List<PullTaskVO> pullTaskVOList = phoneApi.taskPull();
        log.info("电话任务拉取数据(雨绮):{}", pullTaskVOList);
        return pullTaskVOList;
    }
    /**
     * æ‚£è€…问题结果记录(电话)
     */
    @ApiOperation("患者问题结果记录(电话)")
    @PostMapping("/saveQuestionAnswerPhone")
    public AjaxResult saveQuestionAnswerPhone(@RequestBody ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
        log.info("患者问题结果记录(电话):{}", serviceSubTaskDetailReq);
        return toAjax(phoneApi.saveQuestionAnswerPhone(serviceSubTaskDetailReq));
    }
    /**
     * é€šè¯è®°å½•回调(上海)
     */
    @ApiOperation("通话记录回调(上海)")
    @PostMapping("/recordAcceptshanghai")
    public AjaxResult recordAccepshanghait(@RequestBody ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
        log.info("通话记录回调(上海):{}", serviceSubTaskDetailReq);
        return toAjax(phoneApi.saveQuestionAnswerPhone(serviceSubTaskDetailReq));
    }
    /**
     * é€šè¯è®°å½•回调(雨绮)
     */
    @ApiOperation("通话记录回调(雨绮)")
    @PostMapping("/recordAccept")
    public AjaxResult recordAccept(@RequestBody PhoneCallRecordVO phoneCallRecordVO) {
        log.info("通话记录回调(雨绮):{}", phoneCallRecordVO);
        phoneApi.recordAccept(phoneCallRecordVO);
        return success();
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/controller/ShPhoneController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,56 @@
package com.ruoyi.phone.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.phone.api.PhoneApi;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
import com.ruoyi.phone.domain.PhoneCallResultYQVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
 * ä¸‰æ±‡ç”µè¯æµç¨‹å›žè°ƒ Controller。
 *
 * <h3>URL ä¸Žæ¨¡å—化前完全一致</h3>
 * åŽŸå®žçŽ°æ”¾åœ¨ {@code ruoyi-admin} çš„ {@code ServicePhoneController}
 * ï¼ˆ{@code /smartor/shphone/getFirstQues}、{@code /smartor/shphone/processResust}),
 * å½’属电话模块后整体搬到这里,<b>路径不变</b>,三汇平台无需改回调地址。
 * å‰©ä¸‹é‚£ä¸ª {@code /smartor/shphone/addOutboundInstance}(外呼实例配置)依赖 smartor çš„
 * {@code IServicePhoneService},不属于电话会话链路,仍留在 ruoyi-admin。
 *
 * @author ls
 * @date 2026-08-26
 */
@Slf4j
@Api(description = "三汇电话流程服务")
@RestController
@RequestMapping("/smartor/shphone")
public class ShPhoneController extends BaseController {
    @Autowired
    private PhoneApi phoneApi;
    @ApiOperation("根据电话号码,获取首个问题")
    @PostMapping("/getFirstQues")
    public Map<String, Object> getFirstQues(@RequestBody PhoneCallReqYQVO phoneCallReqYQVO) {
        log.info("根据电话号码,获取首个问题?{}", phoneCallReqYQVO);
        PhoneCallBackSHVO phoneCallBackSHVO = phoneApi.getFirstQues(phoneCallReqYQVO);
        return success(phoneCallBackSHVO);
    }
    @ApiOperation("三汇 ASR ç»“果处理")
    @PostMapping("/processResust")
    public Map<String, Object> processResust(@RequestBody PhoneCallResultYQVO resultYQVO) {
        log.info("三汇 ASR ç»“果处理 (三汇)?{}", resultYQVO);
        PhoneCallBackSHVO phoneCallBackSHVO = phoneApi.processResust(resultYQVO);
        return success(phoneCallBackSHVO);
    }
}
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallBackSHVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PhoneCallBackSHVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallBackVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PhoneCallBackVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallBackYQVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PhoneCallBackYQVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallRecordVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PhoneCallRecordVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallReqYQVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PhoneCallReqYQVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PhoneCallResultYQVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PhoneCallResultYQVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/PullTaskVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PullTaskVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-phone/src/main/java/com/ruoyi/phone/domain/QuestionMessage.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/QuestionMessage.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.phone.domain;
import lombok.Data;
ruoyi-phone/src/main/java/com/ruoyi/phone/service/IPhoneService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,78 @@
package com.ruoyi.phone.service;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallBackVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PhoneCallRecordVO;
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
import com.ruoyi.phone.domain.PhoneCallResultYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
import java.util.List;
/**
 * ç”µè¯ä¸šåŠ¡Service接口(原 {@code IServiceSubtaskService} é‡Œçš„电话部分)。
 * <p>
 * âš ï¸ <b>这是模块内部接口</b>,其它模块请注入 {@code com.ruoyi.phone.api.PhoneFacade}({@code PhoneApi}),
 * ä»¥ä¾¿è‡ªåŠ¨èŽ·å¾—å¼€å…³é™çº§ä¸Žç»Ÿä¸€å…¥å£ã€‚
 *
 * @author ruoyi
 * @date 2024-08-08
 */
public interface IPhoneService {
    /**
     * ç”µè¯å›žè°ƒä»»åŠ¡(上海):语音识别结果上报,驱动多轮问答
     *
     * @param phoneCallBackVO å›žè°ƒå…¥å‚
     */
    void phoneCallBack(PhoneCallBackVO phoneCallBackVO);
    /**
     * ç”µè¯ASR通话回调(雨绮)
     *
     * @param phoneCallReqYQVO å›žè°ƒå…¥å‚
     * @return ä¸‹ä¸€æ­¥æ’­æŠ¥å†…容
     */
    PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO);
    /**
     * é›¨ç»®ä»»åŠ¡æ‹‰å–ï¼šæŒ‰å°æ—¶é…é¢ä»Ž redis é˜Ÿåˆ—取待外呼子任务
     *
     * @return å¾…外呼任务列表
     */
    List<PullTaskVO> taskPull();
    /**
     * ç”µè¯ä¾§æ‚£è€…问题结果落库(service_subtask_detail)
     *
     * @param serviceSubTaskDetailReq æ˜Žç»†å…¥å‚
     * @return è½åº“条数
     */
    Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq);
    /**
     * é€šè¯è®°å½•回调(雨绮):无人接听 / å·²å®Œæˆ / æ‹’访
     *
     * @param phoneCallRecordVO é€šè¯è®°å½•
     * @return å¤„理结果(固定 1)
     */
    Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO);
    /**
     * æ ¹æ®ç”µè¯å·ç èŽ·å–é¦–ä¸ªé—®é¢˜(三汇)
     *
     * @param phoneCallReqYQVO å…¥å‚
     * @return å­ä»»åŠ¡ä¸Žé¦–é¢˜ä¿¡æ¯
     */
    PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO);
    /**
     * ä¸‰æ±‡ASR结果处理
     *
     * @param phoneCallResultYQVO ASR ç»“æžœ
     * @return ä¸‹ä¸€é¢˜å†…容
     */
    PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO);
}
ruoyi-phone/src/main/java/com/ruoyi/phone/service/impl/PhoneServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,2056 @@
package com.ruoyi.phone.service.impl;
import com.alibaba.fastjson2.JSONObject;
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.core.service.SubtaskOptionMatchApi;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.*;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallBackVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PhoneCallRecordVO;
import com.ruoyi.phone.domain.PhoneCallReqYQVO;
import com.ruoyi.phone.domain.PhoneCallResultYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
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.task.domain.IvrTaskVisitResult;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.template.domain.IvrLibaExtemplatescript;
import com.ruoyi.template.domain.IvrTaskTemplate;
import com.ruoyi.template.domain.IvrTaskTemplateScript;
import com.ruoyi.template.domain.IvrTaskTemplateScriptVO;
import com.ruoyi.template.domain.IvrTaskTemplateTargetoption;
import com.ruoyi.template.domain.IvrTaskTemplateVO;
import com.ruoyi.template.domain.SvyLibScriptCategory;
import com.ruoyi.template.service.IIvrLibaExtemplatescriptService;
import com.ruoyi.template.service.IIvrTaskTemplateScriptService;
import com.ruoyi.template.service.IIvrTaskTemplateService;
import com.ruoyi.template.service.IIvrTaskTemplateTargetoptionService;
import com.ruoyi.template.service.ISvyLibScriptCategoryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
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 java.util.stream.Collectors;
/**
 * ç”µè¯ä¸šåŠ¡Service实现(原 {@code ruoyi-task} é‡Œ {@code ServiceSubtaskServiceImpl} çš„电话部分)。
 *
 * <h3>这个类是怎么来的</h3>
 * é€è¡Œä»Ž {@code ServiceSubtaskServiceImpl} æ¬å‡ºï¼Œåªåšä¸¤ç±»æœºæ¢°æ”¹å†™ï¼Œ<b>业务分支一行未动</b>:
 * <ol>
 *   <li><b>数据访问换入口</b>:原 {@code serviceSubtaskMapper} / {@code serviceSubtaskDetailMapper} /
 *       {@code serviceTaskVisitResultMapper} / {@code serviceTaskService} / {@code traceService}
 *       çš„调用,统一改为 {@link TaskSubtaskApi}(表归 {@code ruoyi-task},非归属模块不许写这些表);</li>
 *   <li><b>跨模块读取换入口</b>:模板话术改走 {@code ruoyi-template} çš„ service æŽ¥å£
 *       ï¼ˆ{@code IIvrTaskTemplateService} / {@code IIvrTaskTemplateScriptService} /
 *       {@code IIvrTaskTemplateTargetoptionService} / {@code IIvrLibaExtemplatescriptService} /
 *       {@code ISvyLibScriptCategoryService});AI é€‰é¡¹åŒ¹é…æ”¹èµ° ruoyi-common å¥‘约
 *       {@link SubtaskOptionMatchApi}(实现在 {@code ruoyi-task},原 {@code matchOptionIndex} ä»ç•™åœ¨é‚£è¾¹ï¼‰ã€‚</li>
 * </ol>
 *
 * <h3>会话状态在哪里</h3>
 * ä¸€é€šç”µè¯çš„进行状态(当前题号、已问遍数、静默次数、分数、是否已挂机、结束语等)
 * <b>全部放在 Redis</b>(key å‰ç¼€æ˜¯æœ¬é€šç”µè¯çš„ uuid æˆ–"子任务ID-手机号"),
 * è¿™äº› key çš„æ‹¼å†™ã€TTL、读写顺序与原实现完全一致 â€”— ç”µè¯å›žè°ƒæ˜¯å¤–部平台驱动的、
 * è¿›ç¨‹é‡å¯åŽè¦ç»§ç»­ï¼Œæ‰€ä»¥è¿™äº› key å°±æ˜¯"业务状态机",迁移时不能动。
 *
 * <h3>被刻意删掉的东西</h3>
 * åŽŸ {@code ServiceSubtaskServiceImpl#getSmsCompensate(Long)}("电话的补偿方法(老)")没有搬:
 * å®ƒçš„两个调用点({@code phoneCallBackYQ} é‡Œ"患者挂断"、{@code recordAccept} é‡Œ"无人接听")
 * åœ¨æ¨¡å—化前就已被注释掉,属死代码;搬过来只会把 {@code service_out_path} /
 * çŸ­ä¿¡å‘送方 / {@code pub_key} ä¸€ä¸²ä¾èµ–拖进电话模块。如需恢复电话补偿,请按
 * {@code doc/电话模块化_调研与实施方案.md} çš„说明走"归属模块 API"补齐。
 *
 * @author ruoyi
 * @date 2024-08-08
 */
@Slf4j
@Service
public class PhoneServiceImpl implements IPhoneService {
    /**
     * ä»»åŠ¡æ•°æ®è®¿é—® API:子任务 / å­ä»»åŠ¡æ˜Žç»† / ä»»åŠ¡è®¿é—®ç»“æžœ / å¼‚常跟踪。
     * <p>
     * è¿™äº›è¡¨å½’ {@code ruoyi-task},本模块不写它们的 SQL,只经这个 API è¯»å†™ã€‚
     */
    @Autowired
    private TaskSubtaskApi taskSubtaskApi;
    /**
     * å‘送方式状态(service_subtask_preachform):记录"电话"这一通道的发送状态与备注。
     * è¯¥è¡¨å½’ ruoyi-inhospvisit,由 smartor ä¾§é€‚配实现本端口。
     */
    @Autowired
    private TaskPreachformPort taskPreachformPort;
    /**
     * å½•音文件下载(雨绮通话记录回调里把当天录音从 FTP æ‹‰å›žæœ¬åœ°ï¼‰
     */
    @Autowired
    private TaskFtpPort taskFtpPort;
    /**
     * å‚数配置读取({@code sys.voice.match.ai.url}:AI é€‰é¡¹åŒ¹é…æœåŠ¡åœ°å€ï¼ŒæŒ‰æœºæž„é…ç½®ï¼‰
     */
    @Autowired
    private TaskSysConfigPort taskSysConfigPort;
    /**
     * AI é€‰é¡¹åŒ¹é…å¥‘约(ruoyi-common,实现在 ruoyi-task,即原 {@code matchOptionIndex})
     */
    @Autowired
    private SubtaskOptionMatchApi subtaskOptionMatchApi;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IIvrTaskTemplateService ivrTaskTemplateService;
    @Autowired
    private IIvrTaskTemplateScriptService iIvrTaskTemplateScriptService;
    @Autowired
    private IIvrTaskTemplateTargetoptionService ivrTaskTemplateTargetoptionService;
    @Autowired
    private IIvrLibaExtemplatescriptService ivrLibaExtemplatescriptService;
    @Autowired
    private ISvyLibScriptCategoryService svyLibScriptCategoryService;
    /**
     * é›¨ç»®ä»»åŠ¡æ‹‰å–æ—¶å›žå¡«çš„ ASR å›žè°ƒåœ°å€
     */
    @Value("${ASRCallBackPath}")
    private String ASRCallBackPath;
    /**
     * é›¨ç»®å¤–呼账号
     */
    @Value("${app_key_yq}")
    private String app_key_yq;
    /**
     * ä¸Šæµ·é€šé“短信补偿串解密用({@code saveQuestionAnswerPhone} çš„ param1/param2)
     */
    @Value("${pri_key}")
    private String pri_key;
    /**
     * é›¨ç»®æŒ‚机接口地址
     */
    @Value("${hangup}")
    private String hangup;
    /**
     * å½•音本地根目录({@code ruoyi.profile})
     */
    @Value("${ruoyi.profile}")
    private String profile;
    /**
     * è¯­éŸ³æ–‡ä»¶å¯¹å¤–访问前缀(存库前把 FTP ç»å¯¹è·¯å¾„改写成前端可访问路径)
     */
    @Value("${voicePathPrefix}")
    private String voicePathPrefix;
    /**
     * æ¯å°æ—¶å¯å¤–呼量
     */
    @Value("${phoneUpEveryHour}")
    private Integer phoneUpEveryHour;
    /**
     * åœæ­¢å¤–呼的时刻(小时,默认 20 ç‚¹ï¼‰
     */
    @Value("${phoneEndHour}")
    private Integer phoneEndHour;
    @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<String, Object> map = redisCache.getCacheObject(phoneCallBackVO.getUuid());
        ServiceSubtask serviceSubtask = (ServiceSubtask) map.get("ServiceSubtask");
        List<IvrTaskTemplateScriptVO> IvrTaskTemplateScriptVOs = (List<IvrTaskTemplateScriptVO>) map.get("IvrTaskTemplateScriptVO");
        //将uuid更新到数据库中
        serviceSubtask.setSenduuid(phoneCallBackVO.getUuid());
        taskSubtaskApi.updateSubtask(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()) {
                    ServiceSubtask ss = new ServiceSubtask();
                    ss.setId(serviceSubtask.getId());
                    ss.setSendstate(5L);
                    ss.setRemark("电话无响应");
                    taskSubtaskApi.updateSubtask(ss);
                    //没有下一题了,就挂断电话,播放结束语
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "hangup", 1, 120, TimeUnit.MINUTES);
                    phoneUtils.ttsPlayback(ivrTaskTemplateVO.getRevisitAfter(), phoneCallBackVO.getUuid());
                    return;
                } else {
                    //有下一题
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "noVoice", 0, 120, TimeUnit.MINUTES);
                    IvrTaskTemplateScriptVO nextQuestion = getNextQuestion(IvrTaskTemplateScriptVOs, nowQuestion);
                    // é—®é¢˜ï¼Œ  åŽ»è°ƒç”¨"tts合成和播放"接口
                    String date = simpleDateFormat1.format(new Date());
                    log.info("去调用tts合成和播放接口: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                    phoneUtils.ttsPlayback(nowQuestion.getIvrtext(), 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.getIvrtext(), 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());
                }
                log.error("PCB--getQuestionText问题为:{},UUID:{}", nowQuestion.getIvrtext(), phoneCallBackVO.getUuid());
                if (StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && matcher2.matches() && StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && matcher.matches() || StringUtils.isEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && matcher2.matches() || StringUtils.isEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && matcher.matches()) {
                    //说明匹配正确了
                    //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法
                    nowQuestion.getIvrTaskScriptTargetoptionList().get(j).setIsUserOperation(1);
                    ivrTaskTemplateTargetoptionService.updateIvrTaskTemplateTargetoption(nowQuestion.getIvrTaskScriptTargetoptionList().get(j));
                    //将静默置为0
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "noVoice", 0, 120, TimeUnit.MINUTES);
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "mateNum", 0, 120, TimeUnit.MINUTES);
                    //将患者的回签写进表里
                    IvrTaskVisitResult serviceTaskVisitResult = DtoConversionUtils.sourceToTarget(serviceSubtask, IvrTaskVisitResult.class);
                    serviceTaskVisitResult.setId(null);
                    serviceTaskVisitResult.setQuestion(nowQuestion.getIvrtext());
                    serviceTaskVisitResult.setPatientAnswer(phoneCallBackVO.getTextResult());
                    serviceTaskVisitResult.setCreateTime(new Date());
                    serviceTaskVisitResult.setOptionResult(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetvalue());
                    taskSubtaskApi.insertVisitResult(serviceTaskVisitResult);
                    //将匹配到的标识改成true
                    isppd = true;
                    //获取下一题
                    Long nextQuestion = nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getNextQuestion();
                    for (IvrTaskTemplateScriptVO script : IvrTaskTemplateScriptVOs) {
                        if (script.getTargetid() == nextQuestion) {
                            QuestionMessage questionMessage = new QuestionMessage();
                            questionMessage.setNowQuestion(script);
                            questionMessage.setQuestionList(IvrTaskTemplateScriptVOs);
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                            phoneUtils.ttsPlayback(script.getIvrtext(), phoneCallBackVO.getUuid());
                            return;
                        } else if (nextQuestion > IvrTaskTemplateScriptVOs.size()) {
                            //没有下一题了,就结束了
                            ServiceSubtask ss = new ServiceSubtask();
                            ss.setId(serviceSubtask.getId());
                            ss.setSendstate(6L);
                            taskSubtaskApi.updateSubtask(ss);
                            String date = simpleDateFormat1.format(new Date());
                            log.error("没有下一题了,就结束了: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "hangup", 1, 120, TimeUnit.MINUTES);
                            phoneUtils.ttsPlayback(ivrTaskTemplateVO.getRevisitAfter(), phoneCallBackVO.getUuid());
                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            phoneUtils.hangup("", "", ivrTaskTemplateVO.getRevisitAfter(), "", "", "", "", phoneCallBackVO.getUuid());
                            return;
                        }
                    }
                    return;
                } else {
                    //没有匹配上当前option
                    //Targetregex2 ä¸ºfalse,表示在Targetregex2中存在  è¯­å¥ä¸­çš„关键字,这个option就不用再继续匹配了,直接匹配下一个option
                    continue;
                }
            }
            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);
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "mateNum", 0, 120, TimeUnit.MINUTES);
                        String date = simpleDateFormat1.format(new Date());
                        log.info("如果下一题为空.则新的数据返回,并加上感谢语: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                        phoneUtils.ttsPlayback(nextQuestion.getIvrtext(), phoneCallBackVO.getUuid());
                        return;
                    } else {
                        //就可以挂断电话了
                        ServiceSubtask ss = new ServiceSubtask();
                        ss.setId(serviceSubtask.getId());
                        ss.setSendstate(6L);
                        taskSubtaskApi.updateSubtask(ss);
                        String date = simpleDateFormat1.format(new Date());
                        log.info("就可以挂断电话了------: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "hangup", 1, 120, TimeUnit.MINUTES);
                        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<String> list = Arrays.asList(split);
            List<Long> list1 = new ArrayList<>();
            if (StringUtils.isNotEmpty(extemplateID)) {
                for (String str : list) {
                    list1.add(Long.valueOf(str));
                }
                List<IvrLibaExtemplatescript> ivrLibaExtemplatescripts = ivrLibaExtemplatescriptService.queryIvrLibaExtemplatescriptList(list1);
                for (IvrLibaExtemplatescript ivrLibaExtemplatescript : ivrLibaExtemplatescripts) {
                    Matcher matcher = null;
                    if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex())) {
                        Pattern pattern = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex());
                        matcher = pattern.matcher(returnQues.getContent());
                    }
                    Matcher matcher2 = null;
                    if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex2())) {
                        Pattern pattern2 = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex2());
                        matcher2 = pattern2.matcher(returnQues.getContent());
                    }
                    log.error("++++++++++++++++++++++++++通用库是否为空:selfRegex : {} , selfRegex2 : {}", ivrLibaExtemplatescript.getSelfRegex(), ivrLibaExtemplatescript.getSelfRegex2());
                    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()) {
                        QuestionMessage questionMessage = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "returnQues");
                        IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = returnQues.getNowQuestion();
                        ivrTaskTemplateScriptVO.setSubmoduleText(ivrLibaExtemplatescript.getSwitchText());
                        ivrTaskTemplateScriptVO.setSubmoduleVoice(ivrLibaExtemplatescript.getSwitchWav());
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                        if (ivrLibaExtemplatescript.getIsEnd() == 1) {
                            //将问题置空
                            IvrTaskTemplateScriptVO nowQuestion1 = questionMessage.getNowQuestion();
                            nowQuestion1.setScriptContent(null);
                            nowQuestion1.setScriptVoice(null);
                            questionMessage.setNowQuestion(nowQuestion1);
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "isOver", 1, 120, TimeUnit.MINUTES);
                        }
                        //调用"15、tts合成和播放, tts_playback"将结果传回
                        String date = simpleDateFormat1.format(new Date());
                        log.info("调用tts合成和播放------: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                        phoneUtils.ttsPlayback(nowQuestion.getIvrtext() + ivrTaskTemplateScriptVO.getSubmoduleText(), phoneCallBackVO.getUuid());
                    }
                    break;
                }
                String date = simpleDateFormat1.format(new Date());
                log.info("最后的信息回复-: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                phoneUtils.ttsPlayback(nowQuestion.getIvrtext(), phoneCallBackVO.getUuid());
            }
        }
    }
    /**
     * ç”µè¯ASR通话回调(雨绮)
     *
     * @param phoneCallReqYQVO
     */
    @Override
    public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO) {
        //先把该subId从cache_exist中剔除,只要有回调就剔除(目的是,尽快让cache-exist为空,去缓存下一批要打的电话;这里剔除不影响电话的正常业务)
//        redisCache.removeElementFromList("cache-exist", phoneCallReqYQVO.getTaskid());
        //判断是否挂机
        String hangupState = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "- hangup");
        Object cacheObject1 = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
        if (StringUtils.isNotEmpty(hangupState) && hangupState.equals("1") || ObjectUtils.isNotEmpty(cacheObject1) && String.valueOf(cacheObject1).equals("1")) {
            return new PhoneCallBackYQVO();
        }
        if ("AsrCallback".equals(phoneCallReqYQVO.getOperate()) && phoneCallReqYQVO.getEnd_time() != null && phoneCallReqYQVO.getEnd_time() != -1) {
            //处理挂断
            if (phoneCallReqYQVO.getAsrtext().contains("拨叫的用户正忙")) {
                //判断 recordAccept-hungup æ˜¯å¦æœ‰å€¼ï¼Œå¦‚果有,则说明不用处理了,之前已经处理过了(recordAccept-hungup这个主要是给"通话记录用的,")
                Object cacheObject = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
                if (ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
                    ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                    setFailPreachForm(serviceSubtask, "3", "拨叫的用户正忙", "4");
                    return new PhoneCallBackYQVO();
                }
                //如果发送状态为6,就说明已经打完了,也没有必要继续往下走了
                if (StringUtils.isNotEmpty(phoneCallReqYQVO.getTaskid())) {
                    ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                    if (serviceSubtask.getSendstate() == 6L) return new PhoneCallBackYQVO();
                }
                ServiceSubtask serviceSubtask2 = new ServiceSubtask();
                serviceSubtask2.setRemark("电话发送成功(患者挂断)");
                serviceSubtask2.setSenduuid(phoneCallReqYQVO.getUuid());
                serviceSubtask2.setId(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                taskSubtaskApi.updateSubtask(serviceSubtask2);
                Map<String, String> req = new HashMap<>();
                req.put("uuid", phoneCallReqYQVO.getUuid());
                req.put("caller", phoneCallReqYQVO.getPhone());
                HttpUtil.postJsonRequest(hangup, new Gson().toJson(req));
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- hangup", "1", 2, TimeUnit.MINUTES);
                //先把默认的recordAccept-hungup删除,再更新
                redisCache.deleteObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
                redisCache.setCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup", "1", 10, TimeUnit.MINUTES);
                //记录电话发送状态
                setFailPreachForm(serviceSubtask2, "3", "电话发送成功(患者挂断)", "4");
                //这个时候就需要走补偿机制了,先暂定走短信
                //原 getSmsCompensate(...) æœªéšç”µè¯æ¨¡å—化搬迁(模块化前调用点就已注释、属死代码),
                //如需恢复短信补偿,请走 service_out_path / çŸ­ä¿¡å‘送方的归属模块 API,见 doc/电话模块化_调研与实施方案.md
                return new PhoneCallBackYQVO();
            }
        }
        //定义一个分数的变量
        Boolean aBoolean1 = redisCache.hasKey(phoneCallReqYQVO.getUuid() + "SCORE");
        if (!aBoolean1) redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", 0.0, 120, TimeUnit.MINUTES);
        PhoneCallBackYQVO phoneCallBackYQVO = new PhoneCallBackYQVO();
        //channel_create é€šé“创建的时候,可以执行一些其它操作,譬如发个短信之类的;  æˆ‘们的业务可以不用管    PlayEventCallback è¿™ä¸ªæ˜¯æ’­æ”¾è¯­éŸ³çš„,暂时用不到     End_time()= -1或null表示当前的asrtext不是一句完整的话
        if (phoneCallReqYQVO.getOperate().equals("channel_create")) {
            return phoneCallBackYQVO;
        }
        //PlayEventCallback è¿™ä¸ªæ˜¯æ’­æ”¾è¯­éŸ³çš„    playstart:放音开始(问题播报开始)    playstop: æ”¾éŸ³ç»“束(问题播报结束)
        if (phoneCallReqYQVO.getOperate().equals("PlayEventCallback")) {
            String cacheJSY = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "- jsy");
            if (phoneCallReqYQVO.getOperate().equals("PlayEventCallback") && phoneCallReqYQVO.getPlaystop() == false) {
                //判断redis中是否有结束语
                if (StringUtils.isEmpty(cacheJSY)) {
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop", false, 120, TimeUnit.MINUTES);
                    log.error("PlayEventCallbackPlaystop的值为-------:{}", false);
                }
            } else {
                //如果结束语不为空,则要挂电话了
                if (StringUtils.isNotEmpty(cacheJSY)) {
                    Map<String, String> req = new HashMap<>();
                    req.put("uuid", phoneCallReqYQVO.getUuid());
                    req.put("caller", phoneCallReqYQVO.getPhone());
//                    HttpUtils.sendPost(hangup, new Gson().toJson(req));
                    HttpUtil.postJsonRequest(hangup, new Gson().toJson(req));
                    ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                    //记录电话发送状态
                    setFailPreachForm(serviceSubtask, "3", "通话正常结束", "9");
                    //删除结束语的缓存
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "- jsy");
                } else {
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop", true, 120, TimeUnit.MINUTES);
                    log.error("-------PlayEventCallbackPlaystop的值为:{}", true);
                }
            }
            return phoneCallBackYQVO;
        }
        //获取放音是否结束
        Object isPlayEventOverObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
        if (ObjectUtils.isNotEmpty(isPlayEventOverObj)) {
            Boolean isPlayEventOver = (Boolean) isPlayEventOverObj;
            if (!isPlayEventOver) {
                return phoneCallBackYQVO;
            }
        }
        //通过子任务ID获取到模板信息
        ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(Long.valueOf(phoneCallReqYQVO.getTaskid()));
        IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
        //获取模板问题
        IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
        ivrTaskTemplateScript.setTemplateID(serviceSubtask.getTemplateid());
        List<IvrTaskTemplateScript> 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);
            //如果不存在,就把当前的UUID做为key,放到对象中去
            redisCache.setCacheObject(phoneCallReqYQVO.getUuid(), phoneCallReqYQVO, 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.getIvrtext();
                log.error("SilentCallback的问题内容scriptContent:{}", scriptContent);
                phoneCallBackYQVO.setValue(getObject(serviceSubtask, scriptContent));
                //将静默次数加1
                Integer noVoiceNum = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "noVoice");
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "noVoice", noVoiceNum + 1, 120, TimeUnit.MINUTES);
                return phoneCallBackYQVO;
            } else {
                log.error("静默次数达到,挂掉电话:{}", num);
                ServiceTask serviceTask1 = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                ServiceSubtask ss = new ServiceSubtask();
                ss.setId(serviceSubtask.getId());
                ss.setSendstate(5L);
                ss.setRemark("电话静默");
                taskSubtaskApi.updateSubtask(ss);
                //大与等于的话,直接挂断
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                phoneCallBackYQVO.setValue(serviceTask1.getJsy());
                //将结果写到detail中
                ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate));
                serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                //去redis中,把该子任务ID删除
                Long id = serviceSubtask.getId();
                //先更新一下分数
                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);
                //记录状态
                setFailPreachForm(serviceSubtask, "3", "静默次数达到" + num + "次", "4");
                Map<String, String> map = delRedisValue(null, id.toString());
                if (ObjectUtils.isNotEmpty(map)) redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                //在redis中保存一下结束语,在调用挂电话的方法时删除
                ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
            }
            return phoneCallBackYQVO;
        } else if ("AsrCallback".equals(phoneCallReqYQVO.getOperate()) && phoneCallReqYQVO.getEnd_time() != null && phoneCallReqYQVO.getEnd_time() != -1) {
            //做一个标识,用来在通话详情接口里,判断是否有人接电话(value=2,表示有人接)
            redisCache.deleteObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
            redisCache.setCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup", "2", 10, TimeUnit.MINUTES);
            // å…ˆå¯¹é€šè¯è¿›è¡Œé€šç”¨åº“匹配一次
            String extemplateID = ivrTaskTemplate.getSubmoduleID();
            if (StringUtils.isNotEmpty(extemplateID)) {
                String[] split = extemplateID.split(",");
                List<String> list = Arrays.asList(split);
                List<Long> list1 = new ArrayList<>();
                for (String str : list) {
                    list1.add(Long.valueOf(str));
                }
                List<IvrLibaExtemplatescript> ivrLibaExtemplatescripts = ivrLibaExtemplatescriptService.queryIvrLibaExtemplatescriptList(list1);
                IvrLibaExtemplatescript les = null;
                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());
                        phoneCallBackYQVO.setCommonValue(ivrLibaExtemplatescript.getSwitchText());
                        les = ivrLibaExtemplatescript;
                    }
                    break;
                }
                log.info("是否需要挂机:{}", les);
                if (ObjectUtils.isNotEmpty(les)) {
                    //对匹配的结果处理一下(看看是不是需要挂机)
                    if (les.getIsEnd() != null && les.getIsEnd() == 1) {
                        ServiceSubtask ss = new ServiceSubtask();
                        ss.setId(serviceSubtask.getId());
                        ss.setSendstate(6L);
                        ss.setRemark("电话拨打已完成");
                        taskSubtaskApi.updateSubtask(ss);
                        //记录状态
                        setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                        //可以挂机了
                        redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                        //在redis中保存一下结束语,在调用挂电话的方法时删除
                        ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                        redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                        phoneCallBackYQVO.setType("text");
                        phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + les.getSwitchText());
                        //将结果写进detail表
                        ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                        List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                        ivrTaskTemplateScriptVO.setExtemplateText(les.getSwitchText());
                        serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate));
                        serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                        serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                        serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                        saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                        return phoneCallBackYQVO;
                    }
                }
            }
            //如果是文本回调
            //根据问题ID获取该问题的类型
            if (ivrTaskTemplateScriptVO.getScriptType().equals("1")) {
                //用来标记,是否有匹配上的
                Integer flag = 0;
                //1-AI识别
                String url = taskSysConfigPort.selectConfigValue("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());
                    if (aiMatchResult != null) {
                        return aiMatchResult;
                    }
                } else {
                    //是选择题
                    for (int j = 0; j < ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().size(); j++) {
                        log.error("phoneCallReqYQVO.getAsrtext()的值为:{}", phoneCallReqYQVO.getAsrtext());
                        if (StringUtils.isEmpty(phoneCallReqYQVO.getAsrtext())) {
                            continue;
                        }
                        boolean matchedFlag = false;
                        //包含
                        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()) {
                            matchedFlag = true;
                        }
                        //说明匹配正确了
                        if (matchedFlag) {
                            //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法
                            log.info("匹配正确了吗--------------");
                            ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).setIsUserOperation(1);
                            ivrTaskTemplateTargetoptionService.updateIvrTaskTemplateTargetoption(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j));
                            //将患者的回签写进service_subtask_detail中
                            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                            ivrTaskTemplateScriptVO.setQuestionResult(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getOptiondesc());
                            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
                            //修改一下语音路径(改成前端可以访问的,存到数据库中)
                            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                                String[] split = questionvoice.split("\\\\");
                                if (split.length > 0) {
                                    String lastPart = split[split.length - 1];
                                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                                }
                            }
                            serviceSubtaskDetailList.add(serviceSubtaskDetail);
                            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                            serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                            serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                            //判断一下当前的问题是不是满意度问题,并且dutyDeptCode是否有值,并且选项是不是异常选项,如果全符合,则往满意度问题异常表新增
                            if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getDutyDeptCode()) && 1 == ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getIsabnormal()) {
                                ServiceSubtaskDetailTrace subtaskDetailTrace = new ServiceSubtaskDetailTrace();
                                //用taskid、subid和scriptid去获取detailid
                                ServiceSubtaskDetail ssd = new ServiceSubtaskDetail();
                                ssd.setSubId(serviceSubtask.getId());
                                ssd.setTaskid(serviceSubtask.getTaskid());
                                ssd.setScriptid(StringUtils.isNotEmpty(scriptId) ? Long.valueOf(scriptId) : null);
                                List<ServiceSubtaskDetail> serviceSubtaskDetails = taskSubtaskApi.findSubtaskDetails(ssd);
                                subtaskDetailTrace.setDetailId(CollectionUtils.isNotEmpty(serviceSubtaskDetails) ? serviceSubtaskDetails.get(0).getId() : null);
                                subtaskDetailTrace.setSubId(serviceSubtask.getId());
                                subtaskDetailTrace.setTaskid(serviceSubtask.getTaskid());
                                subtaskDetailTrace.setTemplateid(ivrTaskTemplateScriptVO.getId());
                                subtaskDetailTrace.setTemplatequestionnum(ivrTaskTemplateScriptVO.getScriptno());
                                subtaskDetailTrace.setSwitchid(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getId());
                                subtaskDetailTrace.setQuestiontext(ivrTaskTemplateScriptVO.getScriptContent());
                                subtaskDetailTrace.setQuestionvoice(null);
                                subtaskDetailTrace.setCategoryname(ivrTaskTemplateScriptVO.getScriptAssortname());
                                subtaskDetailTrace.setCategoryid(ivrTaskTemplateScriptVO.getScriptAssortid());
                                //获取所有选项
                                String optionDescStr = Optional.ofNullable(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()).filter(list -> !list.isEmpty()).map(list -> list.stream().map(IvrTaskTemplateTargetoption::getOptiondesc).filter(Objects::nonNull).collect(Collectors.joining("&"))).orElse("");
                                subtaskDetailTrace.setTargetid(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getId());
                                subtaskDetailTrace.setTargetvalue(optionDescStr);
                                subtaskDetailTrace.setMatchedtext(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getOptiondesc());
                                subtaskDetailTrace.setValueType(serviceSubtaskDetails.get(0).getValueType());
                                subtaskDetailTrace.setTemplateType(1);
                                SvyLibScriptCategory svyLibScriptCategory = svyLibScriptCategoryService.selectSvyLibScriptCategoryById(ivrTaskTemplateScriptVO.getScriptAssortid());
                                subtaskDetailTrace.setType(svyLibScriptCategory.getType());
                                subtaskDetailTrace.setScriptid(ivrTaskTemplateScriptVO.getId());
                                subtaskDetailTrace.setAsrtext(phoneCallReqYQVO.getAsrtext());
                                subtaskDetailTrace.setRecordpath(phoneCallReqYQVO.getRecordpath());
                                subtaskDetailTrace.setPatid(serviceSubtask.getPatid());
                                JSONObject patdescJson = new JSONObject();
                                patdescJson.put("sendname", serviceSubtask.getSendname());
                                patdescJson.put("phone", serviceSubtask.getPhone());
                                patdescJson.put("age", serviceSubtask.getAge());
                                patdescJson.put("sex", serviceSubtask.getSex() != null ? serviceSubtask.getSex() == 1 ? "男" : "女" : null);
                                subtaskDetailTrace.setPatdesc(patdescJson.toJSONString());
                                subtaskDetailTrace.setTodeptcode(ivrTaskTemplateScriptVO.getDutyDeptCode());
                                subtaskDetailTrace.setTodeptname(ivrTaskTemplateScriptVO.getDutyDeptName());
                                subtaskDetailTrace.setOrgid(serviceSubtask.getOrgid());
                                subtaskDetailTrace.setHandleFlag("0");
                                subtaskDetailTrace.setGuid(phoneCallReqYQVO.getGuid());
                                subtaskDetailTrace.setCreateTime(new Date());
                                subtaskDetailTrace.setUpdateTime(new Date());
                                subtaskDetailTrace.setOrgid(phoneCallReqYQVO.getOrgid());
                                taskSubtaskApi.insertDetailTrace(subtaskDetailTrace);
                            }
                            //判断一下,这个选项结果是不是还有继续问下去的必要,例如选项结果是别人不想继续回答问题,就要结束掉
                            if (ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getIsEnd() == 1) {
                                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                                //在redis中保存一下结束语,在调用挂电话的方法时删除
                                ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                                phoneCallBackYQVO.setType("text");
                                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                                //记录状态
                                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                                return phoneCallBackYQVO;
                            }
                            flag = 1;
                            //将当前前的播报状态删除,给下一题让位
                            redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
//                        //获取下一题
                            log.error("获取下一题的信息:{}", ivrTaskTemplateScriptVO);
                            if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1") || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) {
                                Long nextQuestion = null;
                                if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1")) {
                                    nextQuestion = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getNextQuestion();
                                    //更新分数
                                    double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    score = BigDecimal.valueOf(score).add(new BigDecimal(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getScore())).doubleValue();
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score);
                                } else {
                                    nextQuestion = ivrTaskTemplateScriptVO.getNextScriptno();
                                    //更新分数
                                    Object obj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    Double score = (obj == null ? new Double(0.00) : new Double(((Double) obj).doubleValue()));
                                    score = BigDecimal.valueOf(score).add(new BigDecimal(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getScore())).doubleValue();
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                                }
                                if (nextQuestion == null || nextQuestion == 0L) {
                                    //如果下一题为空,或者为0,则挂机
                                    ServiceSubtask ss = new ServiceSubtask();
                                    ss.setId(serviceSubtask.getId());
                                    ss.setSendstate(6L);
                                    ss.setRemark("电话拨打已完成");
                                    taskSubtaskApi.updateSubtask(ss);
                                    //记录状态
                                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                                    redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                                    ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                                    phoneCallBackYQVO.setType("text");
                                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                                    //将结果写进detail表
                                    ServiceSubTaskDetailReq ssdReq = new ServiceSubTaskDetailReq();
                                    List<ServiceSubtaskDetail> serviceSubtaskDetailList2 = new ArrayList<>();
                                    serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate));
                                    serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList2);
                                    ssdReq.setGuid(phoneCallReqYQVO.getGuid());
                                    ssdReq.setOrgid(phoneCallReqYQVO.getOrgid());
                                    saveQuestionAnswerPhone(ssdReq);
                                    return phoneCallBackYQVO;
                                }
                                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                                    if (script.getSort() == nextQuestion.intValue()) {
                                        phoneCallBackYQVO.setType("text");
                                        phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                                        String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                                        log.error("下一题问题:{}", scriptContent);
                                        log.error("下一题的子任务是:{}", serviceSubtask);
                                        phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + 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.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                                ServiceTask serviceTask1 = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                                phoneCallBackYQVO.setType("text");
                                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                                //更新一下分数
                                double score = 0.0;
                                Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                if (ObjectUtils.isNotEmpty(scoreObj)) score = (double) scoreObj;
                                serviceSubtask.setScore(String.valueOf(score));
                                serviceSubtask.setFinishtime(new Date());
                                serviceSubtask.setSendstate(6L);
                                serviceSubtask.setRemark("电话拨打已完成");
                                taskSubtaskApi.updateSubtask(serviceSubtask);
                                //记录状态
                                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                                //设置结束语
                                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                                Long id = serviceSubtask.getId();
                                Map<String, String> map = delRedisValue(null, id.toString());
                                log.error("map的值为:{}", map);
                                if (ObjectUtils.isNotEmpty(map))
                                    redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                                //在redis中保存一下结束语,在调用挂电话的方法时删除
                                ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
//                                return phoneCallBackYQVO;
                            }
                        } else {
                            log.info("没有匹配上----------------------------");
                            //flag=0,说明没 åŒ¹é…ä¸Šï¼Œä¹Ÿè¦æŠŠæ‚£è€…说的话记录下来
                            if (j == ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().size() - 1 && flag == 0) {
                                ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                                List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                                ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
                                //修改一下语音路径(改成前端可以访问的,存到数据库中)
                                if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                                    String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                                    String[] split = questionvoice.split("\\\\");
                                    if (split.length > 0) {
                                        String lastPart = split[split.length - 1];
                                        serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                                    }
                                }
                                serviceSubtaskDetailList.add(serviceSubtaskDetail);
                                //如果没有 åŒ¹é…ä¸Šï¼Œè¿™ä¸ªå¿…须为null
                                serviceSubtaskDetailList.get(0).setMatchedtext("");
                                serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                                serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                                serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                                saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                            }
                            continue;
                        }
                    }
                }
                //都没有匹配到
                if (StringUtils.isEmpty(phoneCallBackYQVO.getValue())) {
                    log.info("都没有匹配到-------------------------");
                    Integer count = null;
                    Object countObj = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                    if (ObjectUtils.isNotEmpty(countObj)) count = (Integer) countObj;
                    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) {
                            //如果是最后一道题,或者没有下一题了,就直接挂机
                            ServiceTask serviceTask1 = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                            phoneCallBackYQVO.setType("text");
                            phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                            if (StringUtils.isNotEmpty(phoneCallBackYQVO.getValue()))
                                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + phoneCallBackYQVO.getValue() + serviceTask1.getJsy());
                            else phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                            //更新一下分数
                            Double score = null;
                            Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                            serviceSubtask.setScore(String.valueOf(score));
                            serviceSubtask.setFinishtime(new Date());
                            serviceSubtask.setSendstate(6L);
                            serviceSubtask.setRemark("电话拨打已完成");
                            taskSubtaskApi.updateSubtask(serviceSubtask);
                            //记录状态
                            setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                            //在redis中保存一下结束语,在调用挂电话的方法时删除
                            ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                            redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                            //去redis中,把该子任务ID删除
                            Long id = serviceSubtask.getId();
                            Map<String, String> map = delRedisValue(null, id.toString());
                            if (ObjectUtils.isNotEmpty(map)) {
                                redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            }
//                            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 = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent));
                                    //将该患者的Redis中的题目ID,进行修改
                                    redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES);
                                    //更新一下分数
                                    Double score = null;
                                    Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                                    score = BigDecimal.valueOf(score).add(new BigDecimal(script.getScore())).doubleValue();
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 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, StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent()));
                    }
                }
            } else {
                //不是选择题,直接记录答案,将结果写到detail中
                ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
                //修改一下语音路径(改成前端可以访问的,存到数据库中)
                if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                    String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                    String[] split = questionvoice.split("\\\\");
                    if (split.length > 0) {
                        String lastPart = split[split.length - 1];
                        serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                    }
                }
                serviceSubtaskDetailList.add(serviceSubtaskDetail);
                serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                //    String xh = idSort.split("-")[1];
                //如果选项分支为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 = StringUtils.isNotEmpty(ivrTaskTemplateScript1.getIvrtext()) ? ivrTaskTemplateScript1.getIvrtext() : ivrTaskTemplateScript1.getScriptContent();
                            phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent));
                            redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                            redisCache.setCacheObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone(), ivrTaskTemplateScript1.getId().toString());
                            //更新一下分数
                            Double score = null;
                            Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                            score = BigDecimal.valueOf(score).add(new BigDecimal(ivrTaskTemplateScriptVO.getScore())).doubleValue();
                            redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                        }
                    }
                } else if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                    //没有下一题了,就结束了
                    ServiceTask serviceTask1 = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                    //更新一下分数
                    Double score = null;
                    Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                    serviceSubtask.setScore(String.valueOf(score));
                    serviceSubtask.setFinishtime(new Date());
                    serviceSubtask.setRemark("电话拨打已完成");
                    serviceSubtask.setSendstate(6L);
                    taskSubtaskApi.updateSubtask(serviceSubtask);
                    //记录状态
                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                    //去redis中,把该子任务ID删除
                    Long id = serviceSubtask.getId();
                    Map<String, String> map = delRedisValue(null, id.toString());
                    redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask1.getJsy(), 120, TimeUnit.MINUTES);
                    redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                }
            }
        }
        log.error("phoneCallBackYQVO的返回值是:{}", phoneCallBackYQVO);
        return phoneCallBackYQVO;
    }
    private String getObject(ServiceSubtask serviceSubtask, String scriptContent) {
        Map<String, Map<String, String>> param = getParam(serviceSubtask.getTaskid());
        for (Map<String, String> map : param.values()) {
            for (String key : map.keySet()) {
                scriptContent = scriptContent.replace(key, StringUtils.isNotEmpty(map.get(key)) ? map.get(key) : "");
            }
        }
        if (StringUtils.isEmpty(scriptContent)) {
            return scriptContent;
        }
        scriptContent = scriptContent.replace("${name}", StringUtils.isNotEmpty(serviceSubtask.getSendname()) ? serviceSubtask.getSendname() : "");
        scriptContent = scriptContent.replace("${dzz}", StringUtils.isNotEmpty(serviceSubtask.getAddr()) ? serviceSubtask.getAddr() : "");
        scriptContent = scriptContent.replace("${phone}", StringUtils.isNotEmpty(serviceSubtask.getTelcode()) ? serviceSubtask.getTelcode() : "");
        return scriptContent;
    }
    /**
     * èŽ·å–ä»»åŠ¡é‡Œçš„é€šé…ç¬¦
     *
     * @param taskId
     * @return
     */
    private Map<String, Map<String, String>> getParam(Long taskId) {
        ServiceTask serviceTask = taskSubtaskApi.getTask(taskId);
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Map<String, String>> serviceTaskMap = null;
        try {
            serviceTaskMap = objectMapper.readValue(serviceTask.getTextParam(), Map.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return serviceTaskMap;
    }
    private Map<String, String> delRedisValue(String cache, String id) {
        id = "," + id + ",";
        String val = "";
        String cacheName = "";
        Map<String, String> 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<PullTaskVO> taskPull() {
        //pullTaskVOList用于数据返回
        List<PullTaskVO> pullTaskVOList = new ArrayList<>();
        List<String> cacheList = new ArrayList<>();
        ListOperations<String, String> listOps = redisTemplate.opsForList();
        //必须等ache-exist全部打完才能拉取下一批(主要是怕所有的数据都堆在电话的缓存里,在规定的时间打不完)
//        long existSize = listOps.size("cache-exist");
//        if (existSize > 0) return pullTaskVOList;
        int count = 0;
        long size = listOps.size("cache-0");
        log.info("---------打电话之前缓存中的数据量:{}", size);
        if (size > 0) {
            //phoneCount()  é€šè¿‡ç»™å‡ºçš„æ¯ä¸ªå°æ—¶éœ€è¦æ‰“出的电话量,算出到晚上8点一点能打出多少个电话,获取到总量
            //如果需要立即执行的数据量大于一天要执行的上限,则只取上限的数量,其它的则放回redis中
            count = Math.min(phoneCount(), (int) size);
            if (count != 0) {
                cacheList = listOps.range("cache-0", 0, count - 1);
            } else {
                return null;
            }
        }
        log.info("任务拉取cache-0的值是多少:{}", cacheList);
        //  cache-0为立即发起的,其它的先推迟
        if (!CollectionUtils.isEmpty(cacheList)) {
            pullTaskVOList = getPullTaskList(cacheList, "cache-0");
            // åˆ é™¤å·²å¤„理的数据
            listOps.trim("cache-0", count, -1);
            log.info("打电话之后缓存中的数据量---------:{}", listOps.size("cache-0"));
        } else {
            for (int i = 1; i < 6; i++) {
                //取出从cache-1 åˆ° cache-5的第一个子任务信息
                try {
                    Long size1 = listOps.size("cache-" + i);
                    if (size1 == null || size1 == 0) continue;
                    int count1 = Math.min(phoneCount(), size1.intValue());
                    List<String> value = listOps.range("cache-0" + i, 0, count1 - 1);
                    List<PullTaskVO> pullTaskVOList2 = getPullTaskList(value, "cache-" + i);
                    if (CollectionUtils.isNotEmpty(pullTaskVOList2) && pullTaskVOList2.size() > 0) {
                        pullTaskVOList.addAll(pullTaskVOList2);
                    }
                    //将处理过的数据删除
                    listOps.trim("cache-0" + i, count1, -1);
                } catch (Exception e) {
//                    log.error("看着烦");
                }
            }
        }
        //existList用来记录接下来需要执行的子任务ID
//        List<String> existList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(pullTaskVOList)) {
            //给回调参数赋值
            for (PullTaskVO pullTaskVO : pullTaskVOList) {
//                existList.add(pullTaskVO.getTaskid());
                pullTaskVO.setAsrcallback(ASRCallBackPath);
            }
        }
//        if (existList.size() != 0) redisCache.setCacheListRight("cache-exist", existList);
        log.info("-----------实际拉取的电话数据量:{}", CollectionUtils.isNotEmpty(pullTaskVOList) ? pullTaskVOList.size() : null);
        return pullTaskVOList;
    }
    private List<PullTaskVO> getPullTaskList(List<String> subIds, String cacheName) {
        log.error("任务拉取subIds的值是多少:{}", subIds);
        //pullTaskVOList用于数据返回
        List<PullTaskVO> pullTaskVOList = new ArrayList<>();
        //newValue0用于保存没有处理的子 ä»»åŠ¡
//        String newValue0 = "";
        List<String> newValue0 = new ArrayList<>();
        //根据,获取子任务的ID
//        String[] split = subIds.split(",,");
        for (int i = 0; i < subIds.size(); i++) {
            if (cacheName.equals("cache-0") && i < subIds.size() || !cacheName.equals("cache-0") && i < 1) {
                PullTaskVO pullTaskVO = new PullTaskVO();
                String subId = subIds.get(i).trim();
                ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(Long.valueOf(subId));
                if (ObjectUtils.isEmpty(serviceSubtask)) {
                    log.error("该subId有问题,在表中查询不到:{}", subId);
                    continue;
                }
                if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
                    serviceSubtask.setRemark("手机号为空");
                    serviceSubtask.setSendstate(4L);
                    serviceSubtask.setResult("error");
//                    serviceSubtask.setFinishtime(new Date());
                    taskSubtaskApi.updateSubtask(serviceSubtask);
                    continue;
                }
                ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
                if (ObjectUtils.isEmpty(ivrTaskTemplate)) continue;
                //通过任务模板中的"第一次问题编号"获取第一个问题;
                IvrTaskTemplateScript ivrTaskTemplateScript = null;
                IvrTaskTemplateScript ivrTaskTemplateScript1 = new IvrTaskTemplateScript();
                ivrTaskTemplateScript1.setTemplateID(ivrTaskTemplate.getId());
                List<IvrTaskTemplateScript> 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)) {
                    log.error("---------这个ivrTaskTemplate.getId()的问题为空:{}", ivrTaskTemplate.getId());
                    continue;
                }
                //获取通配符匹配过后的问题
                String scriptContent = ivrTaskTemplateScript.getIvrtext();
                if (StringUtils.isEmpty(scriptContent)) scriptContent = ivrTaskTemplateScript.getScriptContent();
                String scrContent = getObject(serviceSubtask, scriptContent);
                String kcb = serviceTask.getKcb() + "," + scrContent;
                //封装返回数据
                //taskId = å­ä»»åŠ¡ID + é—®é¢˜ID +问题序号
                pullTaskVO.setTaskid(subId);
                pullTaskVO.setAppkey(app_key_yq);
                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);
                if (cacheName.equals("cache-0")) pullTaskVO.setDisplayNo("2");
                else pullTaskVO.setDisplayNo("2");
                pullTaskVOList.add(pullTaskVO);
                redisCache.setCacheObject(subId.trim() + "-" + serviceSubtask.getPhone().trim(), ivrTaskTemplateScript.getId().toString(), 120, TimeUnit.MINUTES);
            } else {
                newValue0.add(subIds.get(i));
                redisCache.setCacheListRight(cacheName, newValue0);
            }
        }
        return pullTaskVOList;
    }
    @Override
    public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
        int i = 0;
        log.info("保存电话回调内容 saveQuestionAnswerPhone:{}", serviceSubTaskDetailReq);
        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));
            ServiceSubtaskEntity ivrTaskSingle = new ServiceSubtaskEntity();
            ivrTaskSingle.setTaskid(tid);
            ivrTaskSingle.setPatid(pid);
            List<ServiceSubtask> selectServiceSubtaskList = taskSubtaskApi.findSubtasks(ivrTaskSingle);
            if (CollectionUtils.isEmpty(selectServiceSubtaskList) || selectServiceSubtaskList.size() == 0) {
                log.error("报错了,selectServiceSubtaskList数据为空了:{}", ivrTaskSingle);
                return 0;
            }
            //随访
            for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubTaskDetailReq.getServiceSubtaskDetailList()) {
                if (serviceSubtaskDetail.getScriptid() != null) {
                    //设置一下问题分类
                    IvrTaskTemplateScript ivrTaskTemplateScript = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptByID(serviceSubtaskDetail.getScriptid());
                    if (ivrTaskTemplateScript != null)
                        serviceSubtaskDetail.setCategoryid(ivrTaskTemplateScript.getScriptAssortid());
                    serviceSubtaskDetail.setIsMydException(ivrTaskTemplateScript.getIsMydException());
                    serviceSubtaskDetail.setDimension(ivrTaskTemplateScript.getDimension());
                }
                serviceSubtaskDetail.setSubId(selectServiceSubtaskList.get(0).getId());
//                serviceSubtaskDetail.setId(UUID.randomUUID().toString());
                serviceSubtaskDetail.setCreateTime(new Date());
                serviceSubtaskDetail.setGuid(selectServiceSubtaskList.get(0).getGuid());
                serviceSubtaskDetail.setOrgid(serviceSubTaskDetailReq.getOrgid());
                serviceSubtaskDetail.setGuid(serviceSubTaskDetailReq.getGuid());
                i = taskSubtaskApi.insertSubtaskDetail(serviceSubtaskDetail);
            }
        } else {
            if (serviceSubTaskDetailReq != null && CollectionUtils.isNotEmpty(serviceSubTaskDetailReq.getServiceSubtaskDetailList())) {
                for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubTaskDetailReq.getServiceSubtaskDetailList()) {
                    if (serviceSubtaskDetail.getScriptid() != null) {
                        //设置一下问题分类
                        IvrTaskTemplateScript ivrTaskTemplateScript = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptByID(serviceSubtaskDetail.getScriptid());
                        if (ivrTaskTemplateScript != null)
                            serviceSubtaskDetail.setCategoryid(ivrTaskTemplateScript.getScriptAssortid());
                    }
                    serviceSubtaskDetail.setCreateTime(new Date());
                    serviceSubtaskDetail.setGuid(serviceSubTaskDetailReq.getGuid());
                    serviceSubtaskDetail.setOrgid(serviceSubTaskDetailReq.getOrgid());
                    //先根据子任务id,任务ID和问题序号查询一下,该问题是否已经有值,如果有值,直接修改,如果为空,直接插入
                    ServiceSubtaskDetail ssd = new ServiceSubtaskDetail();
                    ssd.setSubId(serviceSubtaskDetail.getSubId());
                    ssd.setTaskid(serviceSubtaskDetail.getTaskid());
                    ssd.setScriptid(serviceSubtaskDetail.getScriptid());
                    if (serviceSubtaskDetail.getSubId() != null) {
                        ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(serviceSubtaskDetail.getSubId());
                        ssd.setGuid(serviceSubtask.getGuid());
                        ssd.setOrgid(serviceSubtask.getOrgid());
                    }
                    List<ServiceSubtaskDetail> serviceSubtaskDetails = taskSubtaskApi.findSubtaskDetails(ssd);
                    if (CollectionUtils.isNotEmpty(serviceSubtaskDetails) && StringUtils.isNotEmpty(serviceSubtaskDetails.get(0).getMatchedtext())) {
                        serviceSubtaskDetail.setId(serviceSubtaskDetails.get(0).getId());
                        taskSubtaskApi.updateSubtaskDetail(serviceSubtaskDetail);
                    } else {
                        if (serviceSubtaskDetail.getSubId() != null) {
                            ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(serviceSubtaskDetail.getSubId());
                            serviceSubtaskDetail.setGuid(serviceSubtask.getGuid());
                            serviceSubtaskDetail.setOrgid(serviceSubtask.getOrgid());
                        }
                        i = taskSubtaskApi.insertSubtaskDetail(serviceSubtaskDetail);
                    }
                }
            }
        }
        return i;
    }
    @Override
    public Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO) {
        Object cacheObject = redisCache.getCacheObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
        if (phoneCallRecordVO.getHangup_cause().equals("NO_USER_RESPONSE") || ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
            //无人接听
            //记录电话发送状态
            ServiceSubtask serviceSubtask = taskSubtaskApi.getSubtask(Long.valueOf(phoneCallRecordVO.getTaskid()));
            setFailPreachForm(serviceSubtask, "3", "无人接听", "4");
            //进行短信补偿
            //原 getSmsCompensate(...) æœªéšç”µè¯æ¨¡å—化搬迁(模块化前调用点就已注释、属死代码),
            //如需恢复短信补偿,请走 service_out_path / çŸ­ä¿¡å‘送方的归属模块 API,见 doc/电话模块化_调研与实施方案.md
            redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
//            redisCache.removeElementFromList("cache-exist", phoneCallRecordVO.getTaskid());
        } else if (ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("2")) {
//        if (phoneCallRecordVO.getAnswered() != null && phoneCallRecordVO.getAnswered() == true) {
            log.error("有人接电话了,并且完了");
            //说明有人接了
            int startIndex = phoneCallRecordVO.getRecord_path().indexOf("voice") + "voice".length() + 1;  // åŠ 1是跳过/符号
            int endIndex = phoneCallRecordVO.getRecord_path().lastIndexOf("/");
            String result = phoneCallRecordVO.getRecord_path().substring(startIndex, endIndex);
            ServiceSubtask serviceSubtask = new ServiceSubtask();
            serviceSubtask.setRemark("电话拨打已完成");
            serviceSubtask.setSendstate(6L);
            serviceSubtask.setId(Long.valueOf(phoneCallRecordVO.getTaskid()));
            serviceSubtask.setFinishtime(new Date());
            serviceSubtask.setUpdateTime(new Date());
            log.error("电话拨打已完成serviceSubtask的值为:{}", serviceSubtask);
            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());
            redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
        } else {
            //患者拒绝随访
            //先把该subId从cache_exist中剔除,只要有回调就剔除(目的是,尽快让cache-exist为空,去缓存下一批要打的电话;这里剔除不影响电话的正常业务)
//            redisCache.removeElementFromList("cache-exist", phoneCallRecordVO.getTaskid());
            ServiceSubtask serviceSubtask = new ServiceSubtask();
            serviceSubtask.setRemark("电话接通成功,患者拒绝随访");
            serviceSubtask.setId(Long.valueOf(phoneCallRecordVO.getTaskid()));
            serviceSubtask.setSendstate(6L);
            taskSubtaskApi.updateSubtask(serviceSubtask);
//            int startIndex = phoneCallRecordVO.getRecord_path().indexOf("voice") + "voice".length() + 1;  // åŠ 1是跳过/符号
//            int endIndex = phoneCallRecordVO.getRecord_path().lastIndexOf("/");
//            String result = phoneCallRecordVO.getRecord_path().substring(startIndex, endIndex);
            //下载文件
//            taskFtpPort.downloadFolder("/" + LocalDate.now().toString(), profile + "/upload/vadio/voice/" + LocalDate.now().toString());
            redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
            ServiceSubtask ss = taskSubtaskApi.getSubtask(serviceSubtask.getId());
            setFailPreachForm(ss, StringUtils.isNotEmpty(ss.getCurrentPreachform()) ? ss.getCurrentPreachform() : "3", "电话接通成功,患者拒绝随访", "5");
        }
        return 1;
    }
    private IvrTaskTemplateScriptVO getNextQuestion(List<IvrTaskTemplateScriptVO> 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) {
        log.info("-----------------phoneCallReqYQVO的值为:{}", phoneCallReqYQVO);
        ServiceSubtaskDetail serviceSubtaskDetail = new ServiceSubtaskDetail();
        serviceSubtaskDetail.setSubId(Long.valueOf(phoneCallReqYQVO.getTaskid()));
        ServiceSubtask serviceSubtask1 = taskSubtaskApi.getSubtask(Long.valueOf(phoneCallReqYQVO.getTaskid()));
        if (serviceSubtask1 != null) {
            serviceSubtaskDetail.setTaskid(serviceSubtask1.getTaskid());
            serviceSubtaskDetail.setPatid(serviceSubtask1.getPatid());
        }
        serviceSubtaskDetail.setTargetid(ivrTaskTemplateScriptVO.getTargetid() != null ? ivrTaskTemplateScriptVO.getTargetid().intValue() : null);
        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.setCategoryid(ivrTaskTemplateScriptVO.getScriptAssortid());
        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.getScriptno());
        serviceSubtaskDetail.setQuestiontext(StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent());
        serviceSubtaskDetail.setQuestionvoice(phoneCallReqYQVO.getRecordpath());
        serviceSubtaskDetail.setCategoryname(ivrTaskTemplateScriptVO.getScriptType());
        serviceSubtaskDetail.setTargetoptions(ivrTaskTemplateScriptVO.getTargetOptions());
        serviceSubtaskDetail.setExtemplateText(ivrTaskTemplateScriptVO.getExtemplateText());
        serviceSubtaskDetail.setScriptid(ivrTaskTemplateScriptVO.getId());
        serviceSubtaskDetail.setLibTemplateid(serviceSubtask.getLibtemplateid() != null ? serviceSubtask.getLibtemplateid().intValue() : null);
        int i = 1;
        for (IvrTaskTemplateTargetoption ivrTaskTemplateTargetoption : ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()) {
            if (ivrTaskTemplateTargetoption.getIsUserOperation() == 1) {
                serviceSubtaskDetail.setMatchedtext(ivrTaskTemplateTargetoption.getTargetvalue());
                serviceSubtaskDetail.setScore(ivrTaskTemplateTargetoption.getScore() != null ? ivrTaskTemplateTargetoption.getScore().toString() : "0");
            }
            serviceSubtaskDetail.setTargetvalue(StringUtils.isEmpty(serviceSubtaskDetail.getTargetvalue()) ? ivrTaskTemplateTargetoption.getTargetvalue() : serviceSubtaskDetail.getTargetvalue() + "&" + ivrTaskTemplateTargetoption.getTargetvalue());
        }
        serviceSubtaskDetail.setAddtime(new Date());
        serviceSubtaskDetail.setIsupload(0L);
        serviceSubtaskDetail.setUploadTime(new Date());
        serviceSubtaskDetail.setDelFlag("0");
        serviceSubtaskDetail.setValueType(ivrTaskTemplateScriptVO.getScriptType());
        return serviceSubtaskDetail;
    }
    //通过当前时间,拉取电话总数
    private Integer phoneCount() {
        // å½“前时间
        LocalTime currentTime = LocalTime.now();
        // ç›®æ ‡æ—¶é—´ï¼ˆæ™šä¸Š 8 ç‚¹ï¼‰
        LocalTime targetTime = LocalTime.of(phoneEndHour, 0);
//        // è®¡ç®—当前时间到目标时间的时长
//        Duration duration = Duration.between(currentTime, targetTime);
//        // èŽ·å–å‰©ä½™æ—¶é—´çš„å°æ—¶æ•°
//        long remainingHours = 0;
//        long remainingMinutes = duration.toMinutes();
//        //如果超过8点,就设置为0
//        //距离结束时间小于20分钟,就不拉取了
//        if (remainingMinutes < 20) remainingHours = 0;
//        else if (remainingMinutes < 60 && remainingMinutes > 20) remainingHours = 1;
//        else if (remainingMinutes > 60) remainingHours = remainingMinutes / 60;
//        // è®¡ç®—剩余可以打的电话数
//        int remainingCalls = (int) (remainingHours * phoneUpEveryHour);
//        return remainingCalls;
        if (currentTime.isBefore(targetTime)) {
            //如果当前时间还在截止时间内,则继续发
            return phoneUpEveryHour;
        }
        return 0;
    }
    /**
     * @param serviceSubtask
     * @param preachform
     * @param remark
     * @param failSendstate
     * @return
     */
    public Boolean setFailPreachForm(ServiceSubtask serviceSubtask, String preachform, String remark, String failSendstate) {
        log.info("setFailPreachForm éšè®¿æµç¨‹å¤„理 serviceSubtask:{}, preachform:{}, remark:{}, failSendstate:{}", serviceSubtask, preachform, remark, failSendstate);
        //将状态设置为失败
        ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
        serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
        serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
        serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
        //获取该患者的发送方式总数
        List<ServiceSubtaskPreachform> spSize = taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        serviceSubtaskPreachform.setPreachform(preachform);
        //1:未发送状态
        serviceSubtaskPreachform.setSendstate("1");
        if (preachform.equals("3")) {
            //电话进入队列后,状态会变成2
            serviceSubtaskPreachform.setSendstate("2");
        }
        List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
            ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0);
            serviceSubtaskPreachform1.setSendstate(failSendstate);
            serviceSubtaskPreachform1.setRemark(remark);
            taskPreachformPort.updateServiceSubtaskPreachform(serviceSubtaskPreachform1);
            //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成5就行了(全失败了)
            if (serviceSubtaskPreachform1.getSort() == spSize.size() && failSendstate.equals("4")) {
                serviceSubtask.setCurrentPreachform(preachform);
                serviceSubtask.setSendstate(5L);
                serviceSubtask.setRemark(remark + ";所有执行方式均未获取结果,任务失败");
                taskSubtaskApi.updateSubtask(serviceSubtask);
                return true;
            } else {
                //加入目前补偿里的 remark
                serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + remark : remark);
                taskSubtaskApi.updateSubtask(serviceSubtask);
            }
        }
        //尽管失败,这里也将当前发送方式继续设置成“电话”,sendstate设置成3;因为ryTask里的补偿的定时任务运行时,会找到这条subtask,并进行处理
        serviceSubtask.setCurrentPreachform(preachform);
        serviceSubtask.setSendstate(3L);
        //如果等于9,则说明电话正常结束了
        if (failSendstate.equals("9")) serviceSubtask.setSendstate(6L);
        serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime()));
        taskSubtaskApi.updateSubtask(serviceSubtask);
        return true;
    }
    private Date getNextVisitTime(Long subid, Long taskid, Date visitTime) {
        ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
        serviceSubtaskPreachform.setSubid(subid);
        serviceSubtaskPreachform.setTaskid(taskid);
        List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
            //没有随访时间则按当天来算--临时
            if (ObjectUtils.isEmpty(visitTime)) {
                visitTime = new Date();
            }
            String compensateTime = serviceSubtaskPreachforms.get(0).getCompensateTime();
            int hoursToAdd = Integer.valueOf(compensateTime) * 24;
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(visitTime);
            calendar.add(Calendar.HOUR_OF_DAY, hoursToAdd);
            Date newDate = calendar.getTime();
            return newDate;
        }
        return visitTime;
    }
    public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO) {
        //用于返回
        PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO();
        //根据手机号获取用户的的子任务信息
        ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
        serviceSubtaskVO.setPhone(phoneCallReqYQVO.getPhone());
        serviceSubtaskVO.setType("1");
        serviceSubtaskVO.setSendstate(2L);
        List<ServiceSubtask> serviceSubtasks = taskSubtaskApi.findSubtasks(serviceSubtaskVO);
        if (CollectionUtils.isNotEmpty(serviceSubtasks)) {
            ServiceSubtask serviceSubtask = serviceSubtasks.get(0);
            IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
            //通过任务模板中的"第一次问题编号"获取第一个问题;
            IvrTaskTemplateScript ivrTaskTemplateScript = null;
            IvrTaskTemplateScript ivrTaskTemplateScript1 = new IvrTaskTemplateScript();
            ivrTaskTemplateScript1.setTemplateID(ivrTaskTemplate.getId());
            List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript1);
            for (IvrTaskTemplateScript ivrTaskTemplateScript2 : ivrTaskTemplateScripts) {
                if (ivrTaskTemplate.getFirstQuestionNum() == Long.valueOf(ivrTaskTemplateScript2.getSort())) {
                    ivrTaskTemplateScript = ivrTaskTemplateScript2;
                    break;
                }
            }
            //获取开场白
            ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
            phoneCallBackSHVO.setSubId(serviceSubtask.getId());
            phoneCallBackSHVO.setScriptId(ivrTaskTemplateScript.getId());
            phoneCallBackSHVO.setSort(ivrTaskTemplateScript.getSort());
            phoneCallBackSHVO.setTaskTemplateid(ivrTaskTemplateScript.getTemplateID());
            phoneCallBackSHVO.setScriptContent(serviceTask.getKcb() + "," + ivrTaskTemplateScript.getScriptContent());
        }
        return phoneCallBackSHVO;
    }
    /**
     * ä¸‰æ±‡ASR结果处理
     *
     * @param phoneCallResultYQVO
     * @return
     */
    public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO) {
        PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO();
        //通过scriptid,获取所有的结果
        String url = taskSysConfigPort.selectConfigValue("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()));
        Integer i = subtaskOptionMatchApi.matchOptionIndex(url, phoneCallResultYQVO.getScriptContent(), phoneCallResultYQVO.getAsrResult(), ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList(), phoneCallResultYQVO.getUuid());
        if (i >= 0) {
            String optiondesc = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(i).getOptiondesc();
            //将患者的回答写进service_subtask_detail中
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ivrTaskTemplateScriptVO.setQuestionResult(optiondesc);
            PhoneCallReqYQVO phoneCallReqYQVO = new PhoneCallReqYQVO();
            phoneCallReqYQVO.setTaskid("" + serviceSubtask.getId());
            phoneCallReqYQVO.setPhone(phoneCallResultYQVO.getPhone());
            phoneCallReqYQVO.setAsrtext(phoneCallResultYQVO.getAsrResult());
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallResultYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallResultYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
            ivrTaskTemplateScript.setTemplateID(ivrTaskTemplateScriptVO.getTemplateID());
            List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript);
            //获取下一题
            for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                    ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                    phoneCallBackSHVO.setScriptContent(serviceTask.getJsy());
                    phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                    phoneCallBackSHVO.setSort(script.getSort());
                    phoneCallBackSHVO.setScriptId(script.getId());
                    phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                    phoneCallBackSHVO.setStatus("0");
                } else if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                    phoneCallBackSHVO.setScriptContent(script.getScriptContent());
                    phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                    phoneCallBackSHVO.setSort(script.getSort());
                    phoneCallBackSHVO.setScriptId(script.getId());
                    phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                    break;
                }
            }
            //把缓存清除
            redisCache.deleteObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
        } else {
            //再将重复询问
            //记录一下这个问题被问题的次数
            Object cacheObject = redisCache.getCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
            if (cacheObject == null || (Integer) cacheObject < ivrTaskTemplate.getMateNum()) {
                // èŽ·å–é€‰é¡¹é›†åˆä¸­çš„ optiondesc ç”¨äºŽå¼•导患者
                List<IvrTaskTemplateTargetoption> optionList = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList();
                String optionDescStr = "";
                if (optionList != null && !optionList.isEmpty()) {
                    optionDescStr = optionList.stream().map(IvrTaskTemplateTargetoption::getTargetvalue).filter(StringUtils::isNotEmpty).collect(Collectors.joining("或"));
                }
                String scriptContent = "请问" + phoneCallResultYQVO.getScriptContent();
                if (StringUtils.isNotEmpty(optionDescStr)) {
                    scriptContent += ",你可以回答:" + optionDescStr;
                }
                phoneCallBackSHVO.setScriptContent(scriptContent);
                phoneCallBackSHVO.setTaskTemplateid(phoneCallResultYQVO.getTaskTemplateid());
                phoneCallBackSHVO.setSort(phoneCallResultYQVO.getSort());
                phoneCallBackSHVO.setScriptId(phoneCallResultYQVO.getScriptId());
                phoneCallBackSHVO.setSubId(phoneCallResultYQVO.getSubId());
                if (cacheObject == null)
                    redisCache.setCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId(), 1, 120, TimeUnit.MINUTES);
                else
                    redisCache.setCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId(), (Integer) cacheObject + 1, 120, TimeUnit.MINUTES);
            } else if ((Integer) cacheObject >= ivrTaskTemplate.getMateNum()) {
                //如果已经问了规定次数,就不问了,继续问下一题(这个要商量一下,是继续问下一题,还是停了,是不是要做个开关)
                IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
                ivrTaskTemplateScript.setTemplateID(ivrTaskTemplateScriptVO.getTemplateID());
                List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript);
                //获取下一题
                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                    if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                        phoneCallBackSHVO.setScriptContent(script.getScriptContent());
                        phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                        phoneCallBackSHVO.setSort(script.getSort());
                        phoneCallBackSHVO.setScriptId(script.getId());
                        phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                        break;
                    }
                }
                //把缓存清除
                redisCache.deleteObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
            }
        }
        return phoneCallBackSHVO;
    }
    public PhoneCallBackYQVO AiMatch(String url, PhoneCallReqYQVO phoneCallReqYQVO, PhoneCallBackYQVO phoneCallBackYQVO, IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO, ServiceSubtask serviceSubtask, IvrTaskTemplate ivrTaskTemplate, List<IvrTaskTemplateScript> ivrTaskTemplateScripts, String scriptId, Integer flag, String uuid) {
        PhoneCallBackYQVO back = null;
        List<IvrTaskTemplateTargetoption> options = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList();
        String voiceText = phoneCallReqYQVO.getAsrtext();
        String questionText = StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent();
        Integer matched = -1;
        log.info("AI匹配:url = {},questionText = {},voiceText = {},options = {},uuid ={}",
                url,questionText,voiceText,options,uuid);
        matched = subtaskOptionMatchApi.matchOptionIndex(url, questionText, voiceText, options, uuid);
        //说明匹配正确了
        if (matched != null && matched >= 0) {
            IvrTaskTemplateTargetoption chosenOption = options.get(matched);
            //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法
            log.info("匹配正确了吗--------------");
            chosenOption.setIsUserOperation(1);
            ivrTaskTemplateTargetoptionService.updateIvrTaskTemplateTargetoption(chosenOption);
            //将患者的回签写进service_subtask_detail中
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ivrTaskTemplateScriptVO.setQuestionResult(chosenOption.getOptiondesc());
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            //判断一下当前的问题是不是满意度问题,并且dutyDeptCode是否有值,并且选项是不是异常选项,如果全符合,则往满意度问题异常表新增
            if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getDutyDeptCode()) && 1 == chosenOption.getIsabnormal()) {
                ServiceSubtaskDetailTrace subtaskDetailTrace = new ServiceSubtaskDetailTrace();
                //用taskid、subid和scriptid去获取detailid
                ServiceSubtaskDetail ssd = new ServiceSubtaskDetail();
                ssd.setSubId(serviceSubtask.getId());
                ssd.setTaskid(serviceSubtask.getTaskid());
                ssd.setScriptid(StringUtils.isNotEmpty(scriptId) ? Long.valueOf(scriptId) : null);
                List<ServiceSubtaskDetail> serviceSubtaskDetails = taskSubtaskApi.findSubtaskDetails(ssd);
                subtaskDetailTrace.setDetailId(CollectionUtils.isNotEmpty(serviceSubtaskDetails) ? serviceSubtaskDetails.get(0).getId() : null);
                subtaskDetailTrace.setSubId(serviceSubtask.getId());
                subtaskDetailTrace.setTaskid(serviceSubtask.getTaskid());
                subtaskDetailTrace.setTemplateid(ivrTaskTemplateScriptVO.getId());
                subtaskDetailTrace.setTemplatequestionnum(ivrTaskTemplateScriptVO.getScriptno());
                subtaskDetailTrace.setSwitchid(chosenOption.getId());
                subtaskDetailTrace.setQuestiontext(ivrTaskTemplateScriptVO.getScriptContent());
                subtaskDetailTrace.setQuestionvoice(null);
                subtaskDetailTrace.setCategoryname(ivrTaskTemplateScriptVO.getScriptAssortname());
                subtaskDetailTrace.setCategoryid(ivrTaskTemplateScriptVO.getScriptAssortid());
                //获取所有选项
                String optionDescStr = Optional.ofNullable(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()).filter(list -> !list.isEmpty()).map(list -> list.stream().map(IvrTaskTemplateTargetoption::getOptiondesc).filter(Objects::nonNull).collect(Collectors.joining("&"))).orElse("");
                subtaskDetailTrace.setTargetid(chosenOption.getId());
                subtaskDetailTrace.setTargetvalue(optionDescStr);
                subtaskDetailTrace.setMatchedtext(chosenOption.getOptiondesc());
                subtaskDetailTrace.setValueType(serviceSubtaskDetails.get(0).getValueType());
                subtaskDetailTrace.setTemplateType(1);
                SvyLibScriptCategory svyLibScriptCategory = svyLibScriptCategoryService.selectSvyLibScriptCategoryById(ivrTaskTemplateScriptVO.getScriptAssortid());
                subtaskDetailTrace.setType(svyLibScriptCategory.getType());
                subtaskDetailTrace.setScriptid(ivrTaskTemplateScriptVO.getId());
                subtaskDetailTrace.setAsrtext(phoneCallReqYQVO.getAsrtext());
                subtaskDetailTrace.setRecordpath(phoneCallReqYQVO.getRecordpath());
                subtaskDetailTrace.setPatid(serviceSubtask.getPatid());
                JSONObject patdescJson = new JSONObject();
                patdescJson.put("sendname", serviceSubtask.getSendname());
                patdescJson.put("phone", serviceSubtask.getPhone());
                patdescJson.put("age", serviceSubtask.getAge());
                patdescJson.put("sex", serviceSubtask.getSex() != null ? serviceSubtask.getSex() == 1 ? "男" : "女" : null);
                subtaskDetailTrace.setPatdesc(patdescJson.toJSONString());
                subtaskDetailTrace.setTodeptcode(ivrTaskTemplateScriptVO.getDutyDeptCode());
                subtaskDetailTrace.setTodeptname(ivrTaskTemplateScriptVO.getDutyDeptName());
                subtaskDetailTrace.setOrgid(serviceSubtask.getOrgid());
                subtaskDetailTrace.setHandleFlag("0");
                subtaskDetailTrace.setGuid(phoneCallReqYQVO.getGuid());
                subtaskDetailTrace.setCreateTime(new Date());
                subtaskDetailTrace.setUpdateTime(new Date());
                subtaskDetailTrace.setOrgid(phoneCallReqYQVO.getOrgid());
                taskSubtaskApi.insertDetailTrace(subtaskDetailTrace);
            }
            //判断一下,这个选项结果是不是还有继续问下去的必要,例如选项结果是别人不想继续回答问题,就要结束掉
            if (chosenOption.getIsEnd() == 1) {
                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                //在redis中保存一下结束语,在调用挂电话的方法时删除
                ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                //记录状态
                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                back = phoneCallBackYQVO;
                return back;
            }
            flag = 1;
            //将当前前的播报状态删除,给下一题让位
            redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
//                        //获取下一题
            log.error("获取下一题的信息:{}", ivrTaskTemplateScriptVO);
            //branchFlag为null时按分支0处理,避免NPE
            if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) {
                Long nextQuestion = null;
                if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag())) {
                    nextQuestion = chosenOption.getNextQuestion();
                    //更新分数
                    double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    score = BigDecimal.valueOf(score).add(new BigDecimal(chosenOption.getScore())).doubleValue();
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score);
                } else {
                    nextQuestion = ivrTaskTemplateScriptVO.getNextScriptno();
                    //更新分数
                    Object obj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    Double score = (obj == null ? new Double(0.00) : new Double(((Double) obj).doubleValue()));
                    score = BigDecimal.valueOf(score).add(new BigDecimal(String.valueOf(chosenOption.getScore()))).doubleValue();
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                }
                if (nextQuestion == null || nextQuestion == 0L) {
                    //如果下一题为空,或者为0,则挂机
                    ServiceSubtask ss = new ServiceSubtask();
                    ss.setId(serviceSubtask.getId());
                    ss.setSendstate(6L);
                    ss.setRemark("电话拨打已完成");
                    taskSubtaskApi.updateSubtask(ss);
                    //记录状态
                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                    redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                    ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                    //明细在本题匹配成功时已落库(见上方 saveQuestionAnswerPhone),这里不再重复保存
                    back = phoneCallBackYQVO;
                    return back;
                }
                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                    if (script.getSort() == nextQuestion.intValue()) {
                        phoneCallBackYQVO.setType("text");
                        phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                        String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                        log.error("下一题问题:{}", scriptContent);
                        log.error("下一题的子任务是:{}", serviceSubtask);
                        phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + 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());
                    }
                }
                //下一题脚本没找到时,兜底重问当前题,避免调用方误判为未处理而退回正则识别
                if (StringUtils.isEmpty(phoneCallBackYQVO.getValue())) {
                    log.error("下一题脚本未找到,重问当前题,nextQuestion:{}", nextQuestion);
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, questionText));
                }
                back = phoneCallBackYQVO;
                return back;
            } else if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                ServiceTask serviceTask1 = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                //更新一下分数
                double score = 0.0;
                Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                if (ObjectUtils.isNotEmpty(scoreObj)) score = (double) scoreObj;
                serviceSubtask.setScore(String.valueOf(score));
                serviceSubtask.setFinishtime(new Date());
                serviceSubtask.setSendstate(6L);
                serviceSubtask.setRemark("电话拨打已完成");
                taskSubtaskApi.updateSubtask(serviceSubtask);
                //记录状态
                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                //设置结束语
                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                Long id = serviceSubtask.getId();
                Map<String, String> map = delRedisValue(null, id.toString());
                log.error("map的值为:{}", map);
                if (ObjectUtils.isNotEmpty(map)) redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                //在redis中保存一下结束语,在调用挂电话的方法时删除
                ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                //结束语已拼进返回值,挂机由平台按返回值执行
                back = phoneCallBackYQVO;
                return back;
            }
        } else {
            //AI没有匹配上:由AI分支自己按"没问清就重问"的次数处理,不再退回正则识别
            log.info("ai没有匹配上----------------------------");
            //没匹配上也要把患者说的话记录下来(matchedtext置空,与正则路径口径一致)
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            //如果没有 åŒ¹é…ä¸Šï¼Œè¿™ä¸ªå¿…须为null
            serviceSubtaskDetailList.get(0).setMatchedtext("");
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            String mateKey = phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid();
            Integer count = null;
            Object countObj = redisCache.getCacheObject(mateKey);
            if (ObjectUtils.isNotEmpty(countObj)) count = (Integer) countObj;
            if (count != null && count >= ivrTaskTemplate.getMateNum()) {
                //没匹配次数已经用满,不能再重问了
                boolean noNextQuestion = ("0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "1".equals(ivrTaskTemplateScriptVO.getBranchFlag()))
                        && (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0);
                if (noNextQuestion) {
                    //已经是最后一题,直接挂机
                    ServiceTask serviceTask1 = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                    //更新一下分数
                    Double score = null;
                    Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                    serviceSubtask.setScore(String.valueOf(score));
                    serviceSubtask.setFinishtime(new Date());
                    serviceSubtask.setSendstate(6L);
                    serviceSubtask.setRemark("电话拨打已完成");
                    taskSubtaskApi.updateSubtask(serviceSubtask);
                    //记录状态
                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                    ServiceTask serviceTask = taskSubtaskApi.getTask(serviceSubtask.getTaskid());
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                    //去redis中,把该子任务ID删除
                    Long id = serviceSubtask.getId();
                    Map<String, String> map = delRedisValue(null, id.toString());
                    if (ObjectUtils.isNotEmpty(map)) {
                        redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                        redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    }
                } else {
                    //还有下一题,按nextScriptno继续提问
                    for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                        if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                            phoneCallBackYQVO.setType("text");
                            phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                            String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                            phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent));
                            //将该患者的Redis中的题目ID,进行修改
                            redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES);
                            //更新一下分数
                            Double score = null;
                            Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                            score = BigDecimal.valueOf(score).add(new BigDecimal(script.getScore())).doubleValue();
                            redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                            //换题成功,没匹配次数清零
                            redisCache.deleteObject(mateKey);
                        }
                    }
                }
            } else {
                //还没问满次数,用noMatchText引导患者重答
                redisCache.setCacheObject(mateKey, count == null ? 1 : count + 1, 120, TimeUnit.MINUTES);
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                phoneCallBackYQVO.setValue(ivrTaskTemplateScriptVO.getNoMatchText() + getObject(serviceSubtask, StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent()));
            }
            back = phoneCallBackYQVO;
            return back;
        }
        return back;
    }
}
ruoyi-phone/src/test/java/com/ruoyi/phone/config/PhoneSwitchCheck.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,192 @@
package com.ruoyi.phone.config;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.phone.api.PhoneApi;
import com.ruoyi.phone.api.PhoneFacade;
import com.ruoyi.phone.domain.PhoneCallBackSHVO;
import com.ruoyi.phone.domain.PhoneCallBackYQVO;
import com.ruoyi.phone.domain.PullTaskVO;
import java.util.List;
/**
 * ç”µè¯æ¨¡å—开关自检 â€”— çº¯ {@code main},不依赖 JUnit / Spring / æ•°æ®åº“。
 *
 * <h3>为什么不用 JUnit</h3>
 * ä¸Žä»»åŠ¡æ¨¡å—çš„ {@code TaskSwitchCheck}、满意度模块的 {@code SatisfactionSwitchCheck} åŒæ¬¾ï¼š
 * æœ¬ä»“离线仓库缺 {@code surefire-junit3:2.12.4},走
 * {@code mvn -o -q -pl ruoyi-phone -am -DskipTests test-compile} + {@code java -cp} è¿™æ¡è·¯ã€‚
 *
 * <h3>断言覆盖</h3>
 * <ol>
 *   <li>sys_config ä¼˜å…ˆäºŽ yml;</li>
 *   <li>yml å…œåº•;参数行缺失 â†’ å›žè½ yml;</li>
 *   <li>非法值 â†’ æŒ‰"不是 false å³å¯ç”¨";</li>
 *   <li>fail-open:读不到配置服务时按启用,且不抛异常;</li>
 *   <li>10 ç§’缓存与 invalidateCache;</li>
 *   <li>拦截器:开启放行、关闭抛 {@link ServiceException} ä¸” message å«
 *       {@code phone.enabled=false} ä¸Ž {@code æŽ¥å£ä¸å¯ç”¨}(并验证 request ä¸º null ä¸ç‚¸ï¼‰ï¼›</li>
 *   <li>守卫路径恰好 8 æ¡ï¼ˆ6 æ¡åœ¨åŽŸ {@code /smartor/serviceSubtask/**} ä¸‹ +
 *       2 æ¡ä¸‰æ±‡ {@code /smartor/shphone/**}),且"归电话的 6 æ¡"与
 *       {@code TaskWebConfig#excludedPhonePaths()} ä¸€ä¸€å¯¹åº”ï¼›</li>
 *   <li>门面:{@link PhoneFacade} æ˜¯ {@link PhoneApi};关闭时各方法降级为空值/空列表且不抛异常。</li>
 * </ol>
 */
public final class PhoneSwitchCheck {
    private static int failed = 0;
    private static int passed = 0;
    private static void check(String key, Object expected, Object actual) {
        boolean ok = expected == null ? actual == null : expected.equals(actual);
        if (ok) {
            passed++;
            System.out.println("  [OK]   " + key + " = " + actual);
        } else {
            failed++;
            System.out.println("  [FAIL] " + key + " æœŸæœ›=" + expected + " å®žé™…=" + actual);
        }
    }
    private static PhoneProperties props(boolean enabled) {
        PhoneProperties p = new PhoneProperties();
        p.setEnabled(enabled);
        return p;
    }
    public static void main(String[] args) {
        System.out.println("========== ç”µè¯æ¨¡å—开关自检 ==========");
        System.out.println("-- 1) sys_config ä¼˜å…ˆäºŽ yml --");
        check("sys_config=true, yml=false -> enabled", true,
                new PhoneSwitch(props(false), "true").isEnabled());
        check("sys_config=false, yml=true -> enabled", false,
                new PhoneSwitch(props(true), "false").isEnabled());
        System.out.println("-- 2) yml å…œåº• --");
        check("sys_config è¡Œç¼ºå¤±, yml=true -> enabled", true,
                new PhoneSwitch(props(true), (String) null).isEnabled());
        check("sys_config è¡Œç¼ºå¤±, yml=false -> enabled", false,
                new PhoneSwitch(props(false), (String) null).isEnabled());
        System.out.println("-- 3) éžæ³•值按「不是 false å³å¯ç”¨ã€--");
        check("sys_config=yes -> enabled", true, new PhoneSwitch(props(false), "yes").isEnabled());
        check("sys_config=FALSE(大小写) -> enabled", false,
                new PhoneSwitch(props(true), "FALSE").isEnabled());
        System.out.println("-- 4) fail-open:取不到配置服务时不抛异常且按 yml --");
        boolean threw = false;
        boolean value = false;
        try {
            value = new PhoneSwitch(props(true), (String) null).isEnabled();
        } catch (Exception e) {
            threw = true;
        }
        check("无配置服务时不抛异常", false, threw);
        check("无配置服务时按 yml=true", true, value);
        System.out.println("-- 5) 10 ç§’缓存与 invalidateCache --");
        PhoneSwitch sw = new PhoneSwitch(props(false), "false");
        check("初始 enabled", false, sw.isEnabled());
        sw.invalidateCache();
        check("invalidateCache åŽä»è¯»åŒä¸€é…ç½®å€¼", false, sw.isEnabled());
        System.out.println("-- 6) æ‹¦æˆªå™¨ --");
        PhoneAvailabilityInterceptor on =
                new PhoneAvailabilityInterceptor(new PhoneSwitch(props(true), "true"));
        boolean ok = false;
        try {
            ok = on.preHandle(null, null, null);
        } catch (Exception ignored) {
        }
        check("启用时放行", true, ok);
        PhoneAvailabilityInterceptor off =
                new PhoneAvailabilityInterceptor(new PhoneSwitch(props(true), "false"));
        ServiceException caught = null;
        try {
            // request ä¼  null:验证拦截器对 null request å®‰å…¨ï¼ˆæµ‹è¯•会这么调)
            off.preHandle(null, null, null);
        } catch (ServiceException e) {
            caught = e;
        }
        check("关闭时抛 ServiceException", true, caught != null);
        String msg = caught == null ? "" : caught.getMessage();
        check("message å« phone.enabled=false", true, msg.contains("phone.enabled=false"));
        check("message å« æŽ¥å£ä¸å¯ç”¨", true, msg.contains("接口不可用"));
        System.out.println("-- 7) å®ˆå«è·¯å¾„ --");
        String[] guarded = PhoneWebConfig.guardedPathPatterns();
        check("守卫路径条数", 8, guarded.length);
        check("含 phoneCallBack", true, contains(guarded, "/smartor/serviceSubtask/phoneCallBack"));
        check("含 phoneCallBackYQ", true, contains(guarded, "/smartor/serviceSubtask/phoneCallBackYQ"));
        check("含 taskPull", true, contains(guarded, "/smartor/serviceSubtask/taskPull"));
        check("含 saveQuestionAnswerPhone", true, contains(guarded, "/smartor/serviceSubtask/saveQuestionAnswerPhone"));
        check("含 recordAcceptshanghai", true, contains(guarded, "/smartor/serviceSubtask/recordAcceptshanghai"));
        check("含 recordAccept", true, contains(guarded, "/smartor/serviceSubtask/recordAccept"));
        check("含 getFirstQues", true, contains(guarded, "/smartor/shphone/getFirstQues"));
        check("含 processResust", true, contains(guarded, "/smartor/shphone/processResust"));
        // å…³é”®ï¼šä¸å¾—误伤任务模块自己的随访端点与外呼实例配置端点
        check("不含 patItem", false, contains(guarded, "/smartor/serviceSubtask/patItem"));
        check("不含 addOutboundInstance", false, contains(guarded, "/smartor/shphone/addOutboundInstance"));
        System.out.println("-- 7.1) å½’电话的 6 æ¡ï¼ˆä»»åŠ¡æ¨¡å—è¦æŽ’é™¤çš„é‚£ 6 æ¡ï¼‰--");
        String[] owned = PhoneWebConfig.subtaskPathsOwnedByPhone();
        check("条数", 6, owned.length);
        for (String p : owned) {
            check("在守卫清单里: " + p, true, contains(guarded, p));
        }
        System.out.println("-- 8) é—¨é¢é™çº§ --");
        PhoneApi facadeOff = new PhoneFacade(props(true), "false", null);
        check("关闭时 isEnabled", false, facadeOff.isEnabled());
        List<PullTaskVO> disabledTasks = facadeOff.taskPull();
        check("关闭时 taskPull è¿”回空列表", true, disabledTasks != null && disabledTasks.isEmpty());
        check("关闭时 saveQuestionAnswerPhone è¿”回 0", 0, facadeOff.saveQuestionAnswerPhone(null));
        PhoneCallBackYQVO disabledYq = facadeOff.phoneCallBackYQ(null);
        check("关闭时 phoneCallBackYQ è¿”回非 null ç©ºå¯¹è±¡", true, disabledYq != null);
        PhoneCallBackSHVO disabledSh = facadeOff.getFirstQues(null);
        check("关闭时 getFirstQues è¿”回非 null ç©ºå¯¹è±¡", true, disabledSh != null);
        boolean facadeThrew = false;
        try {
            facadeOff.phoneCallBack(null);
            facadeOff.recordAccept(null);
            facadeOff.processResust(null);
        } catch (Exception e) {
            facadeThrew = true;
        }
        check("关闭时门面调用不抛异常", false, facadeThrew);
        PhoneApi facadeOn = new PhoneFacade(props(true), "true", null);
        check("开启时 isEnabled", true, facadeOn.isEnabled());
        // å®žçŽ°ç¼ºå¤±ï¼ˆæµ‹è¯•ä¼  null)也按"不可用"降级,不抛异常
        boolean onThrew = false;
        try {
            facadeOn.phoneCallBack(null);
        } catch (Exception e) {
            onThrew = true;
        }
        check("无实现时不抛异常", false, onThrew);
        System.out.println("==================================================");
        System.out.println("通过 " + passed + " é¡¹ï¼Œå¤±è´¥ " + failed + " é¡¹");
        if (failed > 0) {
            System.exit(1);
        }
    }
    private static boolean contains(String[] array, String value) {
        if (array == null) {
            return false;
        }
        for (String s : array) {
            if (s.equals(value)) {
                return true;
            }
        }
        return false;
    }
    private PhoneSwitchCheck() {
    }
}
ruoyi-quartz/pom.xml
@@ -82,6 +82,19 @@
            <artifactId>ruoyi-message</artifactId>
        </dependency>
        <!--
            ä»»åŠ¡æ¨¡å—ï¼ˆservice_task / service_subtask)。
            RyTask / CollectHISServiceImpl ç›´æŽ¥æ³¨å…¥ä»»åŠ¡åŸŸçš„æœåŠ¡ä¸Ž Mapper
            ï¼ˆIServiceSubtaskService、ServiceTaskMapper、ServiceSubtaskMapper ç­‰ï¼‰ï¼Œ
            è¿™äº›å·²ç‹¬å æ¬å…¥ ruoyi-task,故此处需要编译期依赖。
            âš ï¸ ä¸Žä¸Šé¢ ruoyi-othervisit åŒç†ï¼šåŽŸå…ˆåªé  smartor ä¼ é€’带进来,
               ä¸€æ—¦ä¾èµ–图变化就会在编译期(或更糟:运行期)才炸,故显式声明。
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-task</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-quartz/src/main/java/com/ruoyi/quartz/mapper/CollectHISMapper.java
@@ -18,6 +18,7 @@
import java.util.List;
import java.util.Map;
import com.ruoyi.common.core.domain.entity.PatArchive;
@DataSource(value = DataSourceType.SLAVE)
@Mapper
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ICollectHISService.java
@@ -7,6 +7,7 @@
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
public interface ICollectHISService {
ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/CollectHISServiceImpl.java
@@ -12,7 +12,6 @@
import com.ruoyi.quartz.mapper.CollectHISMapper;
import com.ruoyi.quartz.service.ICollectHISService;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.IPatMedOuthospService;
import lombok.extern.slf4j.Slf4j;
@@ -36,6 +35,10 @@
import com.ruoyi.othervisit.domain.PatMedOperation;
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
@Slf4j
@Service
public class CollectHISServiceImpl implements ICollectHISService {
ruoyi-quartz/src/main/java/com/ruoyi/quartz/task/RyTask.java
@@ -33,8 +33,7 @@
import com.ruoyi.message.sms.provider.sltd.ws.MessageSend;
import com.smartor.common.QwenLLMUtil;
import com.smartor.domain.*;
import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.mapper.*;
import com.smartor.service.*;
import lombok.extern.slf4j.Slf4j;
@@ -62,6 +61,21 @@
import com.ruoyi.template.domain.HeLibrary;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.template.api.TemplateFacade;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.mapper.ServiceTaskMapper;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
import com.ruoyi.task.domain.ServiceOutPath;
import com.ruoyi.task.service.IServiceOutPathService;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.ruoyi.task.domain.SendMagParam;
/**
 * å®šæ—¶ä»»åŠ¡è°ƒåº¦æµ‹è¯•
 *
ruoyi-quartz/src/main/resources/mapper/quartz/CollectHISMapper.xml
@@ -32,7 +32,7 @@
    </resultMap>
    <resultMap type="com.smartor.domain.PatArchive" id="PatArchiveResult">
    <resultMap type="com.ruoyi.common.core.domain.entity.PatArchive" id="PatArchiveResult">
        <result property="id" column="id"/>
        <result property="patientno" column="patientno"/>
        <result property="sourcefrom" column="sourcefrom"/>
@@ -549,7 +549,7 @@
        </if>
    </select>
    <select id="selectPatArchiveList" parameterType="com.smartor.domain.PatArchive" resultMap="PatArchiveResult">
    <select id="selectPatArchiveList" parameterType="com.ruoyi.common.core.domain.entity.PatArchive" resultMap="PatArchiveResult">
        <include refid="selectPatArchiveVo"/>
        where 1=1
            <if test="name != null  and name != ''">and name like concat('%', #{name}, '%')</if>
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/domain/row/SatisfactionTaskRow.java
@@ -5,7 +5,7 @@
/**
 * ã€Œéšè®¿æ»¡æ„åº¦æŒ‡æ•°ã€ç»Ÿè®¡ç”¨çš„ {@code service_task} åªè¯»è§†å›¾ã€‚
 * <p>
 * ä¸ºä»€ä¹ˆéœ€è¦è¿™ä¸ªç±»ï¼ˆè€Œä¸æ˜¯ç›´æŽ¥ç”¨ {@code com.smartor.domain.ServiceTask}):
 * ä¸ºä»€ä¹ˆéœ€è¦è¿™ä¸ªç±»ï¼ˆè€Œä¸æ˜¯ç›´æŽ¥ç”¨ {@code com.ruoyi.task.domain.ServiceTask}):
 * <ul>
 *   <li>那个实体属于 smartor çš„**随访任务管理**业务(被大量文件使用,含增删改查),不能搬进本模块;</li>
 *   <li>满意度指数统计只用到它的 4 ä¸ªå­—段(见下);</li>
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/mapper/SatisfactionMapper.java
@@ -22,8 +22,8 @@
/**
 * æ»¡æ„åº¦ç»Ÿè®¡ Mapper æŽ¥å£ã€‚
 * <p>
 * è¿ç§»è¯´æ˜Žï¼šæ–¹æ³•原本散落在 {@code com.smartor.mapper.ServiceSubtaskMapper} ä¸Ž
 * {@code com.smartor.mapper.ServiceSubtaskDetailMapper} ä¸­ï¼Œæ»¡æ„åº¦æ¨¡å—化后集中到此处。
 * è¿ç§»è¯´æ˜Žï¼šæ–¹æ³•原本散落在 {@code com.ruoyi.task.mapper.ServiceSubtaskMapper} ä¸Ž
 * {@code com.ruoyi.task.mapper.ServiceSubtaskDetailMapper} ä¸­ï¼Œæ»¡æ„åº¦æ¨¡å—化后集中到此处。
 * <p>
 * æœ¬æ¨¡å—自带 SQL,<b>直接查询</b> {@code service_subtask} / {@code service_subtask_detail},
 * ä»¥åŠæ¨¡æ¿åˆ†ç±»è¡¨ï¼›ä¸ä¾èµ–任何其它模块的 Mapper。
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/service/SatisfactionSubtaskService.java
@@ -23,7 +23,7 @@
/**
 * æ»¡æ„åº¦ã€Œå­ä»»åŠ¡ç»´åº¦ã€ç»Ÿè®¡æœåŠ¡ã€‚
 * <p>
 * è¿ç§»è¯´æ˜Žï¼šè¿™äº›æ–¹æ³•原属 {@code com.smartor.service.IServiceSubtaskService},
 * è¿ç§»è¯´æ˜Žï¼šè¿™äº›æ–¹æ³•原属 {@code com.ruoyi.task.service.IServiceSubtaskService},
 * ä½†åªæœ‰æ»¡æ„åº¦ç›¸å…³å…¥å£åœ¨ç”¨ï¼ˆå·²é€ä¸ªæ ¸å®žæ— å…¶å®ƒè°ƒç”¨æ–¹ï¼‰ï¼Œæ•…整批搬入满意度模块。
 * æ–¹æ³•签名除 {@link #getDetailRatio} å¤–均<b>一字未改</b>。
 * <p>
@@ -120,7 +120,7 @@
    /**
     * èŽ·å–æ»¡æ„åº¦å¼‚å¸¸é…ç½®é¢˜ç›®ï¼ˆè¯­éŸ³ + é—®å·ï¼‰ï¼Œç”¨äºŽã€Œæ»¡æ„åº¦å¼‚常配置」页面。
     * <p>
     * è¿ç§»è¯´æ˜Žï¼šåŽŸä¸º {@code com.smartor.service.IServiceSubtaskDetailTraceService#getSatisfactionExceptConfigQuestion},
     * è¿ç§»è¯´æ˜Žï¼šåŽŸä¸º {@code com.ruoyi.task.service.IServiceSubtaskDetailTraceService#getSatisfactionExceptConfigQuestion},
     * æœåŠ¡å®žçŽ°è½åœ¨éšè®¿å¼‚å¸¸è·Ÿè¸ª Service é‡Œï¼Œä½†é€»è¾‘完全是满意度分类/题目的查询,
     * æ•…按模块化要求搬入本模块。<b>返回结构、字段名、筛选条件均与原文一致</b>:
     * <pre>
@@ -144,7 +144,7 @@
    /**
     * éšè®¿ç»Ÿè®¡é¡µçš„「满意度指数」:满意度题目总量 / å¡«æŠ¥é‡ / å®Œæˆæ¯”例。
     * <p>
     * è¿ç§»è¯´æ˜Žï¼šåŽŸä¸º {@code com.smartor.service.impl.ServiceSubtaskServiceImpl#getSatisfactionIndex}
     * è¿ç§»è¯´æ˜Žï¼šåŽŸä¸º {@code com.ruoyi.task.service.impl.ServiceSubtaskServiceImpl#getSatisfactionIndex}
     * ï¼ˆç§æœ‰æ–¹æ³•,被 {@code getSfStatisticsJoy} è°ƒç”¨ï¼‰ã€‚
     * <p>
     * âš ï¸ <b>原实现缺陷已原样保留</b>:由于 {@code BigDecimal.add(...)} çš„返回值被丢弃,
ruoyi-satisfaction/src/main/java/com/ruoyi/satisfaction/service/impl/SatisfactionSubtaskServiceImpl.java
@@ -969,7 +969,7 @@
    /**
     * èŽ·å–æ»¡æ„åº¦å¼‚å¸¸é…ç½®é¢˜ç›®ï¼ˆè¯­éŸ³ + é—®å·ï¼‰ã€‚
     * <p>
     * è¿ç§»è¯´æ˜Žï¼šåŽŸå®žçŽ°ä½äºŽ {@code com.smartor.service.impl.ServiceSubtaskDetailTraceServiceImpl},
     * è¿ç§»è¯´æ˜Žï¼šåŽŸå®žçŽ°ä½äºŽ {@code com.ruoyi.task.service.impl.ServiceSubtaskDetailTraceServiceImpl},
     * çŽ°æ•´æ®µæ¬å…¥æœ¬æ¨¡å—ã€‚åŽŸæ¥çš„è°ƒç”¨é“¾æ˜¯ï¼š
     * <pre>
     * è¯­éŸ³åˆ†ç±»ï¼šIvrLibaScriptAssortServiceImpl.selectIvrLibaScriptAssortList  â† æœåŠ¡æ–¹æ³•ï¼Œä¼šæŠŠåˆ†ç±»ã€æ‹¼æˆæ ‘ã€‘ï¼Œåªè¿”å›žé¡¶å±‚
@@ -1109,7 +1109,7 @@
    /**
     * éšè®¿ç»Ÿè®¡é¡µçš„「满意度指数」:满意度题目总量 / å¡«æŠ¥é‡ / å®Œæˆæ¯”例。
     * <p>
     * è¿ç§»è¯´æ˜Žï¼šåŽŸä¸º {@code com.smartor.service.impl.ServiceSubtaskServiceImpl#getSatisfactionIndex}
     * è¿ç§»è¯´æ˜Žï¼šåŽŸä¸º {@code com.ruoyi.task.service.impl.ServiceSubtaskServiceImpl#getSatisfactionIndex}
     * çš„私有方法,被 {@code getSfStatisticsJoy} è°ƒç”¨ã€‚它的取数对象是
     * ã€Œæ»¡æ„åº¦é¢˜ç›®ã€ï¼ˆæŒ‰ {@code ivrCategoryid} / {@code svyCategoryid} è¯†åˆ«ï¼‰ä¸Ž
     * ã€Œæ»¡æ„åº¦å¡«æŠ¥æ˜Žç»†ã€ï¼Œå±žäºŽæ»¡æ„åº¦å£å¾„,故搬入本模块。
ruoyi-task/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>ruoyi</artifactId>
        <groupId>com.ruoyi</groupId>
        <version>3.8.5</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>ruoyi-task</artifactId>
    <description>
        ä»»åŠ¡æ¨¡å—ï¼šservice_task(任务)与 service_subtask(子任务)的唯一实现处。
        æœ¬æ¨¡å—自带 Mapper ä¸Ž SQL,直接读写 service_task / service_subtask;
        å…¶å®ƒæ¨¡å—需要任务数据只能通过 com.ruoyi.task.api è°ƒç”¨ï¼Œ
        æˆ–经下沉到 ruoyi-common çš„契约(VisitTaskCreateApi / ServiceTaskMatchApi /
        SubtaskStatApi / SubtaskOptionMatchApi)取用。
        æ”¯æŒé€šè¿‡ sys_config å‚数表里的 task.enabled æ•´ä½“关闭(运行时读取,改完最多 10 ç§’生效)。
        ä¾èµ–方向:ruoyi-task -> ruoyi-common(绝不依赖 smartor / smartor-suifang /
        ruoyi-system / ruoyi-framework / ruoyi-admin,否则形成循环依赖,Maven ç›´æŽ¥æž„建失败)。
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·ï¼šBaseController / ExcelUtil / PageUtils / Arith / SecurityUtils / DtoConversionUtils
             åŒæ—¶ä¼ é€’带入 pagehelper(含 mybatis)、swagger-annotations、commons-lang3、jackson ç­‰ -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <!-- lombok:ruoyi-common é‡Œæ˜¯ç›´æŽ¥ä¾èµ–,此处显式声明版本,避免注解处理器缺失 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.34</version>
        </dependency>
        <!-- MyBatis:Mapper æŽ¥å£ä¸Ž XML(pagehelper å·²ä¼ é€’带入,这里只是显式化意图) -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <!-- Servlet API:HttpServletResponse(Controller å¯¼å‡ºç”¨ï¼‰ -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <!--
            æ¨¡æ¿æ¨¡å—:任务的模板 / Script / Targetoption å­—段类型来自 template åŸŸ
            ï¼ˆSvyTaskTemplateTargetoption、HeLibraryCountVO、IvrTaskTemplate* ç­‰ï¼‰ã€‚
            æ³¨æ„ï¼šruoyi-template åªä¾èµ– ruoyi-common,不会与 ruoyi-task å½¢æˆå¾ªçŽ¯ä¾èµ–ã€‚
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-template</artifactId>
        </dependency>
        <!--
            ä»¥ä¸‹æ¨¡å—【都只依赖 ruoyi-common(或其上游)】,因此不会与 ruoyi-task å½¢æˆå¾ªçŽ¯ä¾èµ–ï¼š
              Â· ruoyi-satisfaction  ï¼šéšè®¿ç»Ÿè®¡é‡Œçš„「满意度指数」经 SatisfactionFacade å–用
              Â· ruoyi-message       ï¼šçŸ­ä¿¡ / å¾®ä¿¡å‘送方(SmsSenderFactory / WechatSenderFactory)
              Â· ruoyi-inhospvisit / ruoyi-outpatientvisit / ruoyi-othervisit:
                                    éšè®¿ç¼–排门面(InhospVisitFacade / OuthospDataFacade / OtherVisitFacade)
            âš ï¸ ä»ç„¶ã€ä¸å¾—】依赖 smartor / smartor-suifang / ruoyi-system / ruoyi-framework / ruoyi-admin。
               ruoyi-system çš„能力(如 ISysConfigService çš„ 2 å‚ selectConfigByKey)一律经
               com.ruoyi.task.api.spi çš„端口由 smartor ä¾§å®žçŽ°ã€‚
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-satisfaction</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-message</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-inhospvisit</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-outpatientvisit</artifactId>
        </dependency>
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-othervisit</artifactId>
        </dependency>
    </dependencies>
</project>
ruoyi-task/src/main/java/com/ruoyi/task/TaskModule.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package com.ruoyi.task;
/**
 * ä»»åŠ¡æ¨¡å—åŒ…æ ¹ã€‚
 * <p>
 * ç›®å½•约定:
 * <ul>
 *   <li>{@code com.ruoyi.task.api} â€”— <b>唯一对外包</b>,其它模块只能依赖这个包;</li>
 *   <li>{@code controller} / {@code service} / {@code mapper} / {@code domain} â€”— æ¨¡å—内部实现,
 *       å…¶å®ƒæ¨¡å—不应直接引用。</li>
 * </ul>
 * æœ¬æ¨¡å—是 {@code service_task}(任务)与 {@code service_subtask}(子任务)两张表的<b>唯一实现处</b>。
 * <p>
 * æ¨¡å—可通过 {@code sys_config} å‚数表的 {@code task.enabled} æ•´ä½“关闭
 * ï¼ˆè¿è¡Œæ—¶è¯»å–,改完最多 10 ç§’生效);关闭后 {@code com.ruoyi.task.api.TaskFacade} ä»å­˜åœ¨ï¼Œ
 * ä½†æ‰€æœ‰è°ƒç”¨é™çº§ä¸ºç©ºå€¼ï¼›ç›´æŽ¥è®¿é—®ä»»åŠ¡ç«¯ç‚¹çš„è¯·æ±‚ä¼šè¢«æ‹¦æˆªå™¨æ‹¦ä¸‹å¹¶è¿”å›žæ˜Žç¡®çš„"未启用"业务错误。
 * <p>
 * âš ï¸ <b>依赖红线</b>:本模块只依赖 {@code ruoyi-common}。
 * ç»ä¸ä¾èµ– {@code smartor} / {@code smartor-suifang} / {@code ruoyi-system} /
 * {@code ruoyi-framework} / {@code ruoyi-admin} â€”— å¦åˆ™å½¢æˆ
 * {@code smartor -> ruoyi-task -> smartor} çš„循环依赖,Maven ç›´æŽ¥æž„建失败。
 * éœ€è¦å…¶å®ƒæ¨¡å—的能力时,一律在 {@code ruoyi-common} å®šä¹‰ç«¯å£ã€ç”±å¯¹æ–¹æä¾›å®žçŽ°ã€‚
 */
public final class TaskModule {
    /**
     * æ¨¡å—配置前缀(仅作为 yml å…œåº•配置的键前缀;主开关在 {@code sys_config} å‚数表里)
     */
    public static final String PROPERTIES_PREFIX = "task";
    private TaskModule() {
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.task.api;
/**
 * ä»»åŠ¡æ¨¡å—<b>对外接口</b>(其它模块可依赖的稳定入口)。
 *
 * <h3>⚠️ çœŸæ­£çš„跨模块数据面是 ruoyi-common çš„ 4 ä¸ªå¥‘约,不是本接口</h3>
 * ä»»åŠ¡/子任务被其它模块使用的能力,已经按"接口在 ruoyi-common、实现在归属模块"的既有手法
 * ä¸‹æ²‰æˆ 4 ä¸ªå¥‘约,并由本模块实现:
 * <ul>
 *   <li>{@code com.ruoyi.common.core.service.VisitTaskCreateApi} â€”— æŸ¥å·²å‘布长期任务、落库子任务、回写待执行子任务;</li>
 *   <li>{@code com.ruoyi.common.core.service.ServiceTaskMatchApi} â€”— æŒ‰ç§‘室/诊断/手术编码匹配长期任务;</li>
 *   <li>{@code com.ruoyi.common.core.service.SubtaskStatApi} â€”— é—¨è¯Šéšè®¿ç»Ÿè®¡å–"随访人次/人数";</li>
 *   <li>{@code com.ruoyi.common.core.service.SubtaskOptionMatchApi} â€”— AI é€‰é¡¹åŒ¹é…ã€‚</li>
 * </ul>
 * è°ƒç”¨æ–¹<b>直接注入那 4 ä¸ªæŽ¥å£</b>即可,不必经过本接口。
 *
 * <h3>那本接口做什么</h3>
 * åªæš´éœ²"模块是否可用"这一个稳定判断,让调用方能在调任务能力前先行降级,
 * è€Œä¸å¿…知道 {@code TaskSwitch} / {@code sys_config} çš„内部细节。
 *
 * <h3>关闭时的行为</h3>
 * æœ¬æ¨¡å—的端点会被 {@code TaskAvailabilityInterceptor} æ‹¦ä¸‹å¹¶è¿”回明确业务错误;
 * è€Œ {@code isEnabled()} ä¾›å…¶å®ƒæ¨¡å—在<b>调用前</b>判断,避免"拿到 500 æ‰çŸ¥é“关了"。
 */
public interface TaskApi {
    /**
     * ä»»åŠ¡æ¨¡å—æ˜¯å¦å·²å¯ç”¨ï¼ˆè¿è¡Œæ—¶è¯» {@code sys_config} çš„ {@code task.enabled},最多 10 ç§’延迟)
     */
    boolean isEnabled();
}
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskFacade.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package com.ruoyi.task.api;
import com.ruoyi.task.config.TaskSwitch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * ä»»åŠ¡æ¨¡å—å¯¹å¤–é—¨é¢ï¼ˆ<b>常驻 Bean</b>)。
 *
 * <h3>为什么必须"常驻"</h3>
 * æœ¬é—¨é¢<b>永远</b>是 Spring Bean,不随开关状态增删。这样调用方的
 * {@code @Autowired TaskFacade} åœ¨ä»»ä½•开关状态下都<b>不会启动失败</b>
 * â€”— è¿™ä¸Žæ»¡æ„åº¦æ¨¡å—çš„ {@code SatisfactionFacade} æ˜¯åŒä¸€æ¡è®¾è®¡çº¦æŸ
 * ï¼ˆåŽ†å²æ•™è®­ï¼šç”¨ {@code @ConditionalOnProperty} æ‘˜ Bean ä¼šè®©è°ƒç”¨æ–¹å¯åŠ¨å³å¤±è´¥ï¼Œ
 * ä¸”只在启动时求值一次,做不到"改完参数 10 ç§’生效")。
 *
 * <h3>⚠️ ä¸Žæ»¡æ„åº¦é—¨é¢çš„一个差别:本类不实现自己的查询方法</h3>
 * {@code SatisfactionFacade} åŒæ—¶å®žçް {@code SatisfactionApi} å¹¶éœ€
 * {@code filter(api -> api != this)} é˜²è‡ªå¼•用递归 â€”— å› ä¸ºå®ƒè¦è½¬å‘到模块内实现。
 * æœ¬é—¨é¢åªå›žç­”"是否启用",<b>不转发任何查询</b>(查询走 4 ä¸ª ruoyi-common å¥‘约),
 * å› æ­¤ä¸å­˜åœ¨è‡ªå¼•用问题,也就不需要 {@code ObjectProvider} è‡ªè¿‡æ»¤é‚£ä¸€å¥—。
 * å°†æ¥è‹¥ç»™ {@link TaskApi} å¢žåŠ éœ€è¦è½¬å‘çš„æŸ¥è¯¢æ–¹æ³•ï¼Œ<b>必须补上自引用过滤</b>。
 *
 * @see TaskApi
 */
@Slf4j
@Component
public class TaskFacade implements TaskApi {
    private final TaskSwitch taskSwitch;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>必须显式标注 {@code @Autowired}</b>:本类有<b>两个</b>公开构造器
     * ï¼ˆå¦ä¸€ä¸ªç»™æµ‹è¯•/非 Spring çŽ¯å¢ƒç”¨ï¼‰ï¼Œä¸”<b>没有无参构造器</b>。
     * ä¸æ ‡æ³¨çš„话,Spring æ‰¾ä¸åˆ°"唯一的非默认构造器",会退回查找无参构造器,
     * å¯åŠ¨å³æŠ¥ {@code No default constructor found}。
     * <p>
     * è¿™ä¸Žæ»¡æ„åº¦æ¨¡å— {@code SatisfactionFacade} / {@code SatisfactionSwitch} æ˜¯åŒä¸€ä¸ªå‘,
     * è¯¥è§„则由 {@code sql/_probe/verify_spring_beans.py} é™æ€æŠŠå…³
     * ï¼ˆ**本类已登记进该脚本的扫描范围**,见脚本 MODULE é…ç½®ï¼‰ã€‚
     */
    @Autowired
    public TaskFacade(TaskSwitch taskSwitch) {
        this.taskSwitch = taskSwitch;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造:{@code sysConfigValue} å°±æ˜¯
     * {@code sys_config} é‡Œçš„ {@code task.enabled} å€¼ï¼Œ{@code null} è¡¨ç¤º"参数行不存在"。
     */
    public TaskFacade(com.ruoyi.task.config.TaskProperties properties, String sysConfigValue) {
        this(new TaskSwitch(properties, sysConfigValue));
    }
    @Override
    public boolean isEnabled() {
        return taskSwitch.isEnabled();
    }
    /**
     * å–运行时开关(供拦截器复用同一实例、也供测试断言)
     */
    public TaskSwitch getTaskSwitch() {
        return taskSwitch;
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/TaskSubtaskApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package com.ruoyi.task.api;
import com.ruoyi.task.domain.IvrTaskVisitResult;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import java.util.List;
/**
 * ä»»åŠ¡æ¨¡å—çš„<b>子任务数据访问 API</b>:给"需要在归属模块之外读写子任务数据"的模块用。
 *
 * <h3>为什么要有它</h3>
 * æŒ‰ {@code doc/跨模块数据访问原则与收口方案.md} çš„落地口径:
 * <b>写操作(insert/update/delete å½’属表)任何非归属模块一律禁止</b>;
 * {@code service_subtask} / {@code service_subtask_detail} / {@code service_task} ç­‰è¡¨å½’本模块,
 * æ‰€ä»¥éœ€è¦è¿™äº›æ•°æ®çš„æ¨¡å—(当前是 {@code ruoyi-phone})只能经本 API è°ƒç”¨ï¼Œ
 * <b>不许在自己模块里写针对这些表的 SQL</b>。
 *
 * <h3>与其它对外契约的关系</h3>
 * <ul>
 *   <li>{@code com.ruoyi.common.core.service.*}(VisitTaskCreateApi / ServiceTaskMatchApi /
 *       SubtaskStatApi / SubtaskOptionMatchApi):给 smartor / ruoyi-admin / ruoyi-quartz ç”¨çš„
 *       "下沉到 ruoyi-common çš„æŸ¥è¯¢å¥‘约",走的是 smartor ä¾§å®žçŽ°ï¼›</li>
 *   <li>{@link TaskSubtaskApi}(本接口):<b>模块到模块</b>的数据访问入口,
 *       å®žçŽ°å°±åœ¨æœ¬æ¨¡å—å†…ï¼ˆ{@code api.contract.TaskSubtaskApiImpl}),
 *       å› æ­¤åªå…è®¸"模块 â†’ ä»»åŠ¡æ¨¡å—"这一个方向,不会形成循环依赖。</li>
 * </ul>
 *
 * <h3>方法语义</h3>
 * æ¯ä¸ªæ–¹æ³•都是对既有 Mapper/Service çš„<b>原样转发</b>,不含任何额外业务判断
 * ï¼ˆå°¤å…¶ {@link #updateSubtask} ç”¨åŠ¨æ€ SQL åªæ›´æ–°éžç©ºå­—段,语义必须与
 * {@code ServiceSubtaskMapper.updateServiceSubtask} ä¿æŒä¸€è‡´ï¼Œå¦åˆ™ç”µè¯æµç¨‹ä¼šè¯¯æ”¹å­—段)。
 */
public interface TaskSubtaskApi {
    /**
     * æŒ‰å­ä»»åŠ¡ ID å–子任务(原 {@code ServiceSubtaskMapper#selectServiceSubtaskById})
     *
     * @param subId å­ä»»åŠ¡ ID
     * @return å­ä»»åŠ¡ï¼›ä¸å­˜åœ¨è¿”å›ž {@code null}
     */
    ServiceSubtask getSubtask(Long subId);
    /**
     * æŒ‰æ¡ä»¶æŸ¥å­ä»»åŠ¡åˆ—è¡¨ï¼ˆåŽŸ {@code ServiceSubtaskMapper#selectServiceSubtaskList})
     *
     * @param query æŸ¥è¯¢æ¡ä»¶ï¼ˆtaskid / patid / phone / type / sendstate ç­‰ï¼‰
     * @return å­ä»»åŠ¡åˆ—è¡¨
     */
    List<ServiceSubtask> findSubtasks(ServiceSubtaskEntity query);
    /**
     * æ›´æ–°å­ä»»åŠ¡ï¼ˆåŽŸ {@code ServiceSubtaskMapper#updateServiceSubtask},动态 SQL åªæ”¹éžç©ºå­—段)
     *
     * @param subtask å¾…更新对象(必须带 id)
     * @return å—影响行数
     */
    int updateSubtask(ServiceSubtask subtask);
    /**
     * æŒ‰ä»»åŠ¡ ID å–任务(原 {@code IServiceTaskService#selectServiceTaskByTaskid}),
     * ç”µè¯æµç¨‹ç”¨å®ƒå–开场白(kcb)、结束语(jsy)以及话术通配符(textParam)
     *
     * @param taskid ä»»åŠ¡ ID
     * @return ä»»åŠ¡ï¼›ä¸å­˜åœ¨è¿”å›ž {@code null}
     */
    ServiceTask getTask(Long taskid);
    /**
     * æŒ‰æ¡ä»¶æŸ¥å­ä»»åŠ¡æ˜Žç»†ï¼ˆåŽŸ {@code ServiceSubtaskDetailMapper#selectServiceSubtaskDetailList})
     *
     * @param query æŸ¥è¯¢æ¡ä»¶ï¼ˆsubId / taskid / scriptid / guid / orgid ç­‰ï¼‰
     * @return æ˜Žç»†åˆ—表
     */
    List<ServiceSubtaskDetail> findSubtaskDetails(ServiceSubtaskDetail query);
    /**
     * æ–°å¢žå­ä»»åŠ¡æ˜Žç»†ï¼ˆåŽŸ {@code ServiceSubtaskDetailMapper#insertServiceSubtaskDetail})
     *
     * @param detail æ˜Žç»†
     * @return å—影响行数
     */
    int insertSubtaskDetail(ServiceSubtaskDetail detail);
    /**
     * ä¿®æ”¹å­ä»»åŠ¡æ˜Žç»†ï¼ˆåŽŸ {@code ServiceSubtaskDetailMapper#updateServiceSubtaskDetail})
     *
     * @param detail æ˜Žç»†ï¼ˆå¿…须带 id)
     * @return å—影响行数
     */
    int updateSubtaskDetail(ServiceSubtaskDetail detail);
    /**
     * æ–°å¢žä»»åŠ¡è®¿é—®ç»“æžœï¼ˆåŽŸ {@code IvrTaskVisitResultMapper#insertIvrTaskVisitResult}),
     * ç”µè¯æµç¨‹æŠŠæ¯ä¸€é¢˜çš„"问题 + æ‚£è€…原话 + å‘½ä¸­é€‰é¡¹"记进该表
     *
     * @param visitResult è®¿é—®ç»“æžœ
     * @return å—影响行数
     */
    int insertVisitResult(IvrTaskVisitResult visitResult);
    /**
     * æ–°å¢žå­ä»»åŠ¡å¼‚å¸¸è·Ÿè¸ªï¼ˆåŽŸ {@code IServiceSubtaskDetailTraceService#insertServiceSubtaskDetailTtrace}),
     * ç”µè¯æµç¨‹å‘½ä¸­"异常选项且带责任科室"时写一条,供随访异常处理页使用
     *
     * @param trace å¼‚常跟踪记录
     * @return å—影响行数
     */
    int insertDetailTrace(ServiceSubtaskDetailTrace trace);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/ServiceTaskMatchApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,176 @@
package com.ruoyi.task.api.contract;
import com.ruoyi.common.core.service.ServiceTaskMatchApi;
import com.ruoyi.common.core.service.dto.ServiceTaskMatchDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskdept;
import com.ruoyi.task.domain.ServiceTaskdiag;
import com.ruoyi.task.domain.ServiceTaskoper;
import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
import com.ruoyi.task.mapper.ServiceTaskdiagMapper;
import com.ruoyi.task.mapper.ServiceTaskoperMapper;
/**
 * ã€è¿‡æ¸¡å®žçŽ°Â·ä¸´æ—¶ã€‘{@link ServiceTaskMatchApi} åœ¨ smartor ä¾§çš„æ¡¥æŽ¥ã€‚
 *
 * <h3>为什么需要它</h3>
 * é—¨æ€¥è¯Šéšè®¿ï¼ˆ{@code ruoyi-outpatientvisit})与手术随访({@code ruoyi-othervisit})
 * éƒ½è¦"按维度查长期随访任务",而这些查询用的
 * {@link ServiceTaskdeptMapper} / {@link ServiceTaskdiagMapper} / {@link ServiceTaskoperMapper}
 * å±žäºŽ smartor çš„「服务任务」域,被四个随访模块共用,<b>不能</b>跟任何一方搬走。
 * æ‰€ä»¥åœ¨ã€Œéšè®¿ä»»åŠ¡åˆ›å»ºæ¨¡å—ã€è½åœ°ä¹‹å‰ï¼Œå…ˆæŠŠè¿™ä¸‰ä¸ª<b>读</b>查询包一层。
 * <p>
 * <b>将来动作</b>:任务创建模块建好后把它的实现注册进容器,本类整体删除即可,
 * ä¸¤ä¸ªéšè®¿æ¨¡å—<b>一行都不用改</b>。
 *
 * <h3>⚠️ æœ¬ç±»å¿…须保持"纯搬运"</h3>
 * è¿™é‡Œ<b>不得</b>加入去重、排序、默认值补齐、缓存等任何新逻辑 â€”—
 * é€æ–¹æ³•与 {@code selectServiceTask*List} ä¸€ä¸€å¯¹åº”。
 * "查不到时把标志位置 2 å¹¶å†™åŽŸå› "属于随访域业务规则,留在各随访模块里。
 *
 * <h3>逐方法来源(迁移前 @ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原调用位置</th></tr>
 *   <tr><td>{@link #findLongTasksByDept(String, String)}</td>
 *       <td>门诊 {@code PatMedOuthospServiceImpl#dealOutpatientInfo} L394-398
 *           ï¼ˆ{@code longtask=1} + {@code deptCode} + {@code serviceType="3"})</td></tr>
 *   <tr><td>{@link #findLongTasksByDiag(String)}</td>
 *       <td>门诊同方法 L410-413({@code longtask=1} + {@code icd10code})</td></tr>
 *   <tr><td>{@link #findLongTasksByOpcode(String)}</td>
 *       <td>手术 {@code PatMedOperationServiceImpl#dealOperationInfo} L118-121
 *           ï¼ˆ{@code longtask=1} + {@code opcode})</td></tr>
 * </table>
 *
 * <h3>⚠️ ä¸ºä»€ä¹ˆå‚数里有 serviceType å´ä¸ç”±æœ¬ç±»å†™æ­»</h3>
 * é—¨è¯Šçš„科室维度查询传 {@code "3"},但那是<b>门诊的</b>口径,不是本契约的口径。
 * æœ¬ç±»åªè´Ÿè´£æŠŠå®ƒé€ä¼ è¿› {@code service_type=#{serviceType}},
 * å…å¾—多一个调用方时把 {@code "3"} é”™è¯¯åœ°å½“成通用值。
 *
 * @see com.ruoyi.common.core.service.ServiceTaskMatchApi
 */
@Slf4j
@Component
public class ServiceTaskMatchApiImpl implements ServiceTaskMatchApi {
    @Autowired
    private ServiceTaskdeptMapper serviceTaskdeptMapper;
    @Autowired
    private ServiceTaskdiagMapper serviceTaskdiagMapper;
    @Autowired
    private ServiceTaskoperMapper serviceTaskoperMapper;
    /**
     * ç§‘室维度。
     * <p>
     * åŽŸå®žçŽ°é€è¡Œå¯¹åº”ï¼š
     * <pre>
     * ServiceTaskdept serviceTaskdept = new ServiceTaskdept();
     * serviceTaskdept.setLongtask(1L);
     * serviceTaskdept.setDeptCode(patMedOuthosp1.getDeptcode());
     * serviceTaskdept.setServiceType("3");
     * List&lt;ServiceTaskdept&gt; list = serviceTaskdeptMapper.selectServiceTaskdeptList(serviceTaskdept);
     * </pre>
     */
    @Override
    public List<ServiceTaskMatchDto> findLongTasksByDept(String deptCode, String serviceType) {
        ServiceTaskdept q = new ServiceTaskdept();
        q.setLongtask(1L);
        q.setDeptCode(deptCode);
        q.setServiceType(serviceType);
        List<ServiceTaskdept> rows = serviceTaskdeptMapper.selectServiceTaskdeptList(q);
        if (CollectionUtils.isEmpty(rows)) {
            return Collections.emptyList();
        }
        List<ServiceTaskMatchDto> out = new ArrayList<>(rows.size());
        for (ServiceTaskdept r : rows) {
            ServiceTaskMatchDto d = new ServiceTaskMatchDto();
            d.setId(r.getId());
            d.setTaskId(r.getTaskId());
            d.setTaskName(r.getTaskName());
            d.setLongtask(r.getLongtask());
            d.setServiceType(r.getServiceType());
            d.setMatchCode(r.getDeptCode());
            out.add(d);
        }
        return out;
    }
    /**
     * è¯Šæ–­ç»´åº¦ã€‚
     * <p>
     * åŽŸå®žçŽ°é€è¡Œå¯¹åº”ï¼š
     * <pre>
     * ServiceTaskdiag serviceTaskdiag = new ServiceTaskdiag();
     * serviceTaskdiag.setLongtask(1L);
     * serviceTaskdiag.setIcd10code(patMedOuthosp1.getIcd10code());
     * List&lt;ServiceTaskdiag&gt; list = serviceTaskdiagMapper.selectServiceTaskdiagList(serviceTaskdiag);
     * </pre>
     * âš ï¸ æ³¨æ„åŽŸå®žçŽ°<b>没有</b>设 {@code serviceType},本方法保持一致(不补)。
     */
    @Override
    public List<ServiceTaskMatchDto> findLongTasksByDiag(String icd10code) {
        ServiceTaskdiag q = new ServiceTaskdiag();
        q.setLongtask(1L);
        q.setIcd10code(icd10code);
        List<ServiceTaskdiag> rows = serviceTaskdiagMapper.selectServiceTaskdiagList(q);
        if (CollectionUtils.isEmpty(rows)) {
            return Collections.emptyList();
        }
        List<ServiceTaskMatchDto> out = new ArrayList<>(rows.size());
        for (ServiceTaskdiag r : rows) {
            ServiceTaskMatchDto d = new ServiceTaskMatchDto();
            d.setId(r.getId());
            d.setTaskId(r.getTaskId());
            d.setTaskName(r.getTaskName());
            d.setLongtask(r.getLongtask());
            d.setServiceType(r.getServiceType());
            d.setMatchCode(r.getIcd10code());
            out.add(d);
        }
        return out;
    }
    /**
     * æ‰‹æœ¯ç¼–码维度。
     * <p>
     * åŽŸå®žçŽ°é€è¡Œå¯¹åº”ï¼š
     * <pre>
     * ServiceTaskoper serviceTaskoper = new ServiceTaskoper();
     * serviceTaskoper.setLongtask(1L);
     * serviceTaskoper.setOpcode(patMedOperation1.getOpcode());
     * List&lt;ServiceTaskoper&gt; list = serviceTaskoperMapper.selectServiceTaskoperList(serviceTaskoper);
     * </pre>
     * âš ï¸ åŒæ ·<b>不设</b> {@code serviceType},与原实现一致。
     */
    @Override
    public List<ServiceTaskMatchDto> findLongTasksByOpcode(String opcode) {
        ServiceTaskoper q = new ServiceTaskoper();
        q.setLongtask(1L);
        q.setOpcode(opcode);
        List<ServiceTaskoper> rows = serviceTaskoperMapper.selectServiceTaskoperList(q);
        if (CollectionUtils.isEmpty(rows)) {
            return Collections.emptyList();
        }
        List<ServiceTaskMatchDto> out = new ArrayList<>(rows.size());
        for (ServiceTaskoper r : rows) {
            ServiceTaskMatchDto d = new ServiceTaskMatchDto();
            d.setId(r.getId());
            d.setTaskId(r.getTaskId());
            d.setTaskName(r.getTaskName());
            d.setLongtask(r.getLongtask());
            d.setServiceType(r.getServiceType());
            d.setMatchCode(r.getOpcode());
            out.add(d);
        }
        return out;
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/SubtaskOptionMatchApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.task.api.contract;
import com.ruoyi.common.core.service.SubtaskOptionMatchApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import com.ruoyi.task.service.IServiceSubtaskService;
/**
 * ã€è¿‡æ¸¡å®žçŽ°Â·ä¸´æ—¶ã€‘{@link SubtaskOptionMatchApi} åœ¨ smartor ä¾§çš„æ¡¥æŽ¥ã€‚
 *
 * <h3>为什么需要它</h3>
 * æ¨¡æ¿æ¨¡å—çš„ {@code IvrLibaTargetServiceImpl} éœ€è¦ã€ŒAI é€‰é¡¹åŒ¹é…ã€èƒ½åŠ›ï¼Œ
 * è€Œè¯¥èƒ½åŠ›ç›®å‰å®žçŽ°åœ¨ smartor çš„ {@code ServiceSubtaskServiceImpl#matchOptionIndex}
 * ï¼ˆåŒæ–‡ä»¶å†…还有 3 å¤„自有调用,所以不能整体搬走)。
 * æ¨¡æ¿æ¨¡å—不能依赖 smartor,于是按既有手法把接口放 ruoyi-common、实现留在这里。
 *
 * <h3>本类做什么</h3>
 * <b>纯转发</b>,零逻辑改动。
 *
 * <h3>⚠️ æ³›åž‹é€ä¼ </h3>
 * å¥‘约用泛型 {@code <T>} è®©è°ƒç”¨ç‚¹çš„类型保持原样(他们拿到下标后要
 * {@code options.get(i).getOptiondesc()})。这里直接透传 {@code T},
 * smartor ä¾§é‚£ä¸ªæ–¹æ³•按运行时类型序列化,结果与迁移前逐字一致。
 *
 * @see com.ruoyi.common.core.service.SubtaskOptionMatchApi
 */
@Slf4j
@Component
public class SubtaskOptionMatchApiImpl implements SubtaskOptionMatchApi {
    @Autowired
    private IServiceSubtaskService serviceSubtaskService;
    @Override
    public <T> Integer matchOptionIndex(String url, String questionText, String voiceText,
                                        java.util.List<T> options, String uuid) {
        return serviceSubtaskService.matchOptionIndex(url, questionText, voiceText, options, uuid);
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/SubtaskStatApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.task.api.contract;
import com.ruoyi.common.core.service.SubtaskStatApi;
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.core.service.dto.PatMedRes;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
/**
 * ã€è¿‡æ¸¡å®žçŽ°Â·ä¸´æ—¶ã€‘{@link SubtaskStatApi} åœ¨ smartor ä¾§çš„æ¡¥æŽ¥ã€‚
 *
 * <h3>为什么需要它</h3>
 * é—¨è¯Šéšè®¿ç»Ÿè®¡ï¼ˆ{@code selectPatMedOuthospCount})要把"门诊人次/人数"与
 * "随访人次/人数"合并返回,后者查的是 {@code service_subtask}(子任务域)。
 * éšè®¿æ¨¡å—不能依赖 smartor,故按既有套路把这一层"查"下沉成 ruoyi-common å¥‘约。
 *
 * <h3>⚠️ çº¯æ¬è¿</h3>
 * æœ¬æ–¹æ³•只做"转发到原 Mapper",不加工参数、不设默认值。
 * åŽŸå®žçŽ°é‡Œ {@code patMedRes.setRc(count)}(把本域算出的门诊人次塞回去)属门诊域,
 * ç•™åœ¨è°ƒç”¨æ–¹ã€‚
 *
 * <h3>原实现对应</h3>
 * é—¨è¯Š {@code PatMedOuthospServiceImpl#selectPatMedOuthospCount} L345:
 * {@code serviceSubtaskMapper.selectVisitCount(patMedReq)}。
 *
 * <h3>将来动作</h3>
 * ã€Œéšè®¿ä»»åŠ¡åˆ›å»ºæ¨¡å—ã€è½åœ°åŽæŠŠå®ƒçš„å®žçŽ°æ³¨å†Œè¿›å®¹å™¨ï¼Œæœ¬ç±»æ•´ä½“åˆ é™¤å³å¯ã€‚
 */
@Slf4j
@Component
public class SubtaskStatApiImpl implements SubtaskStatApi {
    @Autowired
    private ServiceSubtaskMapper serviceSubtaskMapper;
    @Override
    public PatMedRes countVisitForStat(PatMedReq req) {
        return serviceSubtaskMapper.selectVisitCount(req);
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/TaskSubtaskApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,97 @@
package com.ruoyi.task.api.contract;
import com.ruoyi.task.api.TaskSubtaskApi;
import com.ruoyi.task.domain.IvrTaskVisitResult;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.mapper.IvrTaskVisitResultMapper;
import com.ruoyi.task.mapper.ServiceSubtaskDetailMapper;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
import com.ruoyi.task.service.IServiceTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * {@link TaskSubtaskApi} çš„æ¨¡å—内实现:把调用原样转发给归属本模块的 Mapper / Service。
 *
 * <h3>为什么放在 {@code api.contract} åŒ…</h3>
 * ä¸Ž {@code ServiceTaskMatchApiImpl} / {@code SubtaskStatApiImpl} / {@code SubtaskOptionMatchApiImpl}
 * ç­‰ ruoyi-common å¥‘约的实现放在一起,便于"对外契约 â†’ å®žçް"成对阅读。
 *
 * <h3>行为约定</h3>
 * æœ¬ç±»<b>不做任何业务判断</b>(不校验、不补字段、不改状态),只转发;
 * ç”µè¯æµç¨‹åŽŸæœ‰çš„ä¸šåŠ¡åˆ†æ”¯ä¸€å¾‹ç•™åœ¨ {@code ruoyi-phone},
 * è¿™æ ·"搬迁"是纯搬家,行为与模块化前逐行一致。
 */
@Slf4j
@Component
public class TaskSubtaskApiImpl implements TaskSubtaskApi {
    @Autowired
    private ServiceSubtaskMapper serviceSubtaskMapper;
    @Autowired
    private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper;
    @Autowired
    private IvrTaskVisitResultMapper serviceTaskVisitResultMapper;
    @Autowired
    private IServiceTaskService serviceTaskService;
    @Autowired
    private IServiceSubtaskDetailTraceService traceService;
    @Override
    public ServiceSubtask getSubtask(Long subId) {
        return serviceSubtaskMapper.selectServiceSubtaskById(subId);
    }
    @Override
    public List<ServiceSubtask> findSubtasks(ServiceSubtaskEntity query) {
        return serviceSubtaskMapper.selectServiceSubtaskList(query);
    }
    @Override
    public int updateSubtask(ServiceSubtask subtask) {
        Boolean updated = serviceSubtaskMapper.updateServiceSubtask(subtask);
        return Boolean.TRUE.equals(updated) ? 1 : 0;
    }
    @Override
    public ServiceTask getTask(Long taskid) {
        return serviceTaskService.selectServiceTaskByTaskid(taskid);
    }
    @Override
    public List<ServiceSubtaskDetail> findSubtaskDetails(ServiceSubtaskDetail query) {
        return serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(query);
    }
    @Override
    public int insertSubtaskDetail(ServiceSubtaskDetail detail) {
        return serviceSubtaskDetailMapper.insertServiceSubtaskDetail(detail);
    }
    @Override
    public int updateSubtaskDetail(ServiceSubtaskDetail detail) {
        return serviceSubtaskDetailMapper.updateServiceSubtaskDetail(detail);
    }
    @Override
    public int insertVisitResult(IvrTaskVisitResult visitResult) {
        return serviceTaskVisitResultMapper.insertIvrTaskVisitResult(visitResult);
    }
    @Override
    public int insertDetailTrace(ServiceSubtaskDetailTrace trace) {
        return traceService.insertServiceSubtaskDetailTtrace(trace);
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/contract/VisitTaskCreateApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,310 @@
package com.ruoyi.task.api.contract;
import com.ruoyi.common.core.service.VisitTaskCreateApi;
import com.ruoyi.common.core.service.dto.VisitLongTaskDto;
import com.ruoyi.common.core.service.dto.VisitPendingSubtaskDto;
import com.ruoyi.common.core.service.dto.VisitSubtaskDraft;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.mapper.ServiceTaskMapper;
/**
 * ã€è¿‡æ¸¡å®žçŽ°Â·ä¸´æ—¶ã€‘{@link VisitTaskCreateApi} åœ¨ smartor ä¾§çš„æ¡¥æŽ¥ã€‚
 *
 * <h3>为什么需要它</h3>
 * å‡ºå…¥é™¢éšè®¿è¦"建随访任务",但按你的版图,<b>任务创建将来是独立模块</b>。
 * åœ¨å®ƒè½åœ°ä¹‹å‰ï¼Œå…ˆæŠŠçŽ°æœ‰çš„å†™åº“èƒ½åŠ›ï¼ˆ{@link ServiceSubtaskMapper} /
 * {@code IPatMedInhospService#persistSubtask})原样包一层,
 * è®© {@code ruoyi-inhospvisit} çŽ°åœ¨å°±åªä¾èµ– {@link VisitTaskCreateApi} è¿™ä¸ªæŽ¥å£ã€‚
 * <p>
 * <b>将来动作</b>:新模块建好后,把它的实现注册进容器,
 * æœ¬ç±»æ•´ä½“删除即可,{@code ruoyi-inhospvisit} <b>一行都不用改</b>。
 *
 * <h3>⚠️ æœ¬ç±»å¿…须保持"纯搬运"</h3>
 * è¿™é‡Œ<b>不得</b>加入去重、状态机、默认值补齐、合并更新等任何新逻辑 â€”—
 * é‚£äº›è¯­ä¹‰åœ¨ {@code ruoyi-inhospvisit} çš„ {@code writeInSubTask} é‡Œã€‚
 * æœ¬ç±»åªåšï¼š<b>字段搬运</b> + <b>转发现有 Mapper è°ƒç”¨</b>。
 * é€å­—段对应关系由 {@code sql/_probe/verify_inhospvisit_equivalence.py}(A æ¡£ï¼‰ä¸Ž
 * {@code verify_inhospvisit_semantics.py}(B æ¡£å£å¾„断言)把关。
 *
 * <h3>逐方法来源(迁移前 {@code PatMedInhospServiceImpl},@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现位置</th></tr>
 *   <tr><td>{@link #findPublishedLongTask(Long)}</td><td>L686-728(查 ServiceTask,sendState=2)</td></tr>
 *   <tr><td>{@link #insertSubtask(VisitSubtaskDraft)}</td><td>L823 / L838 / L854 / L862 / L873</td></tr>
 *   <tr><td>{@link #findPendingSubtasks(Long, Long)}</td><td>L760-764、L1325-1328</td></tr>
 *   <tr><td>{@link #updatePendingSubtask(VisitPendingSubtaskDto)}</td><td>L782 / L791 / L812 / L836 / L1349</td></tr>
 * </table>
 *
 * @see com.ruoyi.common.core.service.VisitTaskCreateApi
 */
@Slf4j
@Component
public class VisitTaskCreateApiImpl implements VisitTaskCreateApi {
    @Autowired
    private ServiceTaskMapper serviceTaskMapper;
    @Autowired
    private ServiceSubtaskMapper serviceSubtaskMapper;
    /**
     * æŒ‰ taskid å–「已发布的长期任务」。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆ{@code PatMedInhospServiceImpl} L686-690):
     * <pre>
     * ServiceTask st = new ServiceTask();
     * st.setTaskid(taskid);
     * st.setSendState(2L);
     * List&lt;ServiceTask&gt; serviceTasks = serviceTaskMapper.selectServiceTaskList(st);
     * ServiceTask serviceTask = null;
     * if (CollectionUtils.isEmpty(serviceTasks)) { ...按 type å†™ä¸åŒ flag åŽ return... }
     * else { serviceTask = serviceTasks.get(0); }
     * </pre>
     * è¿™é‡Œåªè´Ÿè´£"查 + å–第一条";"查不到时按 type å†™å“ªä¸ª flag"属于随访域,
     * ç•™åœ¨ {@code writeInSubTask} é‡Œã€‚
     */
    @Override
    public VisitLongTaskDto findPublishedLongTask(Long taskid) {
        ServiceTask query = new ServiceTask();
        query.setTaskid(taskid);
        query.setSendState(2L);
        List<ServiceTask> tasks = serviceTaskMapper.selectServiceTaskList(query);
        if (CollectionUtils.isEmpty(tasks)) {
            return null;
        }
        return toLongTaskDto(tasks.get(0));
    }
    @Override
    public boolean insertSubtask(VisitSubtaskDraft draft) {
        if (draft == null) {
            log.warn("【出入院随访·过渡桥】insertSubtask æ”¶åˆ° null è‰ç¨¿ï¼Œè·³è¿‡");
            return false;
        }
        ServiceSubtask entity = toSubtaskEntity(draft);
        // è½¬å‘现有过渡入口(等价于 serviceSubtaskMapper.insertServiceSubtask(entity) == 1)
        boolean ok = serviceSubtaskMapper.insertServiceSubtask(entity) == 1;
        // useGeneratedKeys ä¼šæŠŠè‡ªå¢žä¸»é”®å›žå¡«åˆ° entity ä¸Šï¼Œå›žä¼ ç»™å‡ºå‚草稿,
        // ä¾›"先 insert å†è¡¥å­—段回写"的那条链路使用(原实现也是复用同一个对象)
        draft.setId(entity.getId());
        return ok;
    }
    @Override
    public List<VisitPendingSubtaskDto> findPendingSubtasks(Long patid, Long taskid) {
        ServiceSubtaskEntity query = new ServiceSubtaskEntity();
        query.setPatid(patid);
        query.setSendstate(2L);
        // åŽŸå®žçŽ°åœ¨ dealInHospInfo(L1325-1328)里【不设 taskid】(按患者全量查),
        // åœ¨ writeInSubTask(L760-764)里【设 taskid】。这里保持同样的可选语义。
        query.setTaskid(taskid);
        List<ServiceSubtask> rows = serviceSubtaskMapper.selectServiceSubtaskList(query);
        if (CollectionUtils.isEmpty(rows)) {
            return Collections.emptyList();
        }
        List<VisitPendingSubtaskDto> out = new ArrayList<>(rows.size());
        for (ServiceSubtask row : rows) {
            out.add(toPendingDto(row));
        }
        return out;
    }
    @Override
    public boolean updatePendingSubtask(VisitPendingSubtaskDto pending) {
        if (pending == null || pending.getId() == null) {
            log.warn("【出入院随访·过渡桥】updatePendingSubtask ç¼ºå°‘ id,跳过。pending={}", pending);
            return false;
        }
        ServiceSubtask entity = new ServiceSubtask();
        // åªæ˜ å°„"原实现会带过去"的字段:
        // åŽŸå®žçŽ°ä¼ çš„æ˜¯ã€æŸ¥è¯¢å‡ºæ¥çš„å®Œæ•´å¯¹è±¡ã€‘+ åœ¨å†…存里改的那几个字段。
        // æŸ¥è¯¢å‡ºæ¥çš„完整对象里,与本方法语义相关的只有 id/taskid/endtime/longSendTime/serviceType/orgid/sendstate/patid,
        // å…¶ä½™å­—段值不变、写回也等于不写,故此处只带这些 + è°ƒç”¨æ–¹æ”¹è¿‡çš„ result/remark/updateBy/updateTime。
        entity.setId(pending.getId());
        entity.setTaskid(pending.getTaskid());
        entity.setSendstate(pending.getSendstate());
        entity.setResult(pending.getResult());
        entity.setRemark(pending.getRemark());
        entity.setUpdateBy(pending.getUpdateBy());
        entity.setUpdateTime(pending.getUpdateTime());
        entity.setIsVisitAgain(pending.getIsVisitAgain());
        // finishtime:出入院侧从不设置(原样保留"不写"),门诊侧会设置
        entity.setFinishtime(pending.getFinishtime());
        return Boolean.TRUE.equals(serviceSubtaskMapper.updateServiceSubtask(entity));
    }
    /**
     * å–任务的 {@code preachformDesc}。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆ{@code PatMedInhospServiceImpl#addServiceSubtaskPreachform} L1063)用的是
     * {@code serviceTaskMapper.selectServiceTaskByTaskid(taskid)} â€”—
     * <b>不带 sendState æ¡ä»¶</b>,且查不到时返回 null。
     * è¿™é‡Œé€å­—照搬,不复用 {@link #findPublishedLongTask}(那个带 {@code sendState=2})。
     */
    @Override
    public String findPreachformDesc(Long taskid) {
        if (taskid == null) {
            return null;
        }
        ServiceTask task = serviceTaskMapper.selectServiceTaskByTaskid(taskid);
        return task == null ? null : task.getPreachformDesc();
    }
    /**
     * æ˜¯å¦å­˜åœ¨ã€ŒæŒ‡å®šçŠ¶æ€ + æŒ‡å®šæœåŠ¡ç±»åž‹ã€çš„å­ä»»åŠ¡ã€‚
     * <p>
     * åŽŸå®žçŽ°ï¼ˆé—¨è¯Š {@code selectPatMedOuthospList} L108-117):调
     * {@code serviceSubtaskMapper.selectServiceSubtaskBySendstate(v)},
     * å‚数是 {@code ServiceSubtaskEntity},设了 {@code patid} / {@code sendstates} / {@code serviceType}。
     * è¿™é‡Œé€å­—照搬,只把"非空"折叠成 {@code boolean}(调用方原本只用它做 if åˆ¤æ–­ï¼‰ã€‚
     *
     * <p>⚠️ ä¸å±žäºŽ {@link #findPendingSubtasks(Long, Long)}:那个固定 {@code sendstate=2}
     * ä¸”按 taskid è¿‡æ»¤ã€æŸ¥ {@code selectServiceSubtaskList},口径不同、不可互替。
     */
    @Override
    public boolean existsSubtaskWithStates(Long patid, java.util.List<Long> sendstates, String serviceType) {
        if (patid == null) {
            return false;
        }
        ServiceSubtaskEntity v = new ServiceSubtaskEntity();
        v.setPatid(patid);
        v.setSendstates(sendstates);
        v.setServiceType(serviceType);
        List<ServiceSubtask> list = serviceSubtaskMapper.selectServiceSubtaskBySendstate(v);
        return !CollectionUtils.isEmpty(list);
    }
    // ==========================================================================
    // å­—段搬运(逐字段对照,勿"顺手精简")
    // ==========================================================================
    private VisitLongTaskDto toLongTaskDto(ServiceTask t) {
        VisitLongTaskDto dto = new VisitLongTaskDto();
        dto.setTaskid(t.getTaskid());
        dto.setTaskName(t.getTaskName());
        dto.setType(t.getType());
        dto.setServiceType(t.getServiceType());
        dto.setTemplateid(t.getTemplateid());
        dto.setTemplatename(t.getTemplatename());
        dto.setLibtemplateid(t.getLibtemplateid());
        dto.setSendDay(t.getSendDay());
        dto.setPreachform(t.getPreachform());
        dto.setPreachformDesc(t.getPreachformDesc());
        dto.setOrgid(t.getOrgid());
        dto.setCampusid(t.getCampusid());
        dto.setHosType(t.getHospType());
        dto.setUpdateBy(t.getUpdateBy());
        dto.setCreateBy(t.getCreateBy());
        return dto;
    }
    /**
     * è‰ç¨¿ â†’ å®žä½“。
     * <p>
     * é€ä¸ª setter å¯¹ç…§è¿ç§»å‰çš„ {@code boxedServiceSubtask} ä¸Ž {@code writeInSubTask},
     * <b>少一个字段就会让对应列静默不更新</b>(XML é‡Œæ˜¯ {@code <if test="xxx != null">}),
     * æ‰€ä»¥è¿™é‡Œåˆ»æ„å†™å…¨ï¼Œä¸åšç²¾ç®€ã€‚
     */
    private ServiceSubtask toSubtaskEntity(VisitSubtaskDraft d) {
        ServiceSubtask e = new ServiceSubtask();
        e.setId(d.getId());
        e.setTaskid(d.getTaskid());
        e.setOrgid(d.getOrgid());
        e.setCampusid(d.getCampusid());
        e.setPatid(d.getPatid());
        e.setSendname(d.getSendname());
        e.setSfzh(d.getSfzh());
        e.setPhone(d.getPhone());
        e.setSex(d.getSex());
        e.setAge(d.getAge());
        e.setInhospid(d.getInhospid());
        e.setNurseId(d.getNurseId());
        e.setNurseName(d.getNurseName());
        e.setDrcode(d.getDrcode());
        e.setDrname(d.getDrname());
        e.setManagementDoctor(d.getManagementDoctor());
        e.setManagementDoctorCode(d.getManagementDoctorCode());
        e.setDeptcode(d.getDeptcode());
        e.setDeptname(d.getDeptname());
        e.setLeavehospitaldistrictcode(d.getLeavehospitaldistrictcode());
        e.setLeavehospitaldistrictname(d.getLeavehospitaldistrictname());
        e.setLeaveicd10code(d.getLeaveicd10code());
        e.setLeavediagname(d.getLeavediagname());
        e.setDiagname(d.getDiagname());
        e.setTemplateid(d.getTemplateid());
        e.setTemplatename(d.getTemplatename());
        e.setLibtemplateid(d.getLibtemplateid());
        e.setStarttime(d.getStarttime());
        e.setEndtime(d.getEndtime());
        e.setLongSendTime(d.getLongSendTime());
        e.setVisitTime(d.getVisitTime());
        e.setSendstate(d.getSendstate());
        e.setSubmit(d.getSubmit());
        e.setServiceType(d.getServiceType());
        e.setDiagType(d.getDiagType());
        e.setHospType(d.getHospType());
        e.setPreachform(d.getPreachform());
        e.setVisitCount(d.getVisitCount());
        e.setIsVisitAgain(d.getIsVisitAgain());
        e.setVisitDeptCode(d.getVisitDeptCode());
        e.setVisitDeptName(d.getVisitDeptName());
        e.setTaskSituation(d.getTaskSituation());
        e.setResult(d.getResult());
        e.setRemark(d.getRemark());
        e.setOperationItemId(d.getOperationItemId());
        e.setCreateBy(d.getCreateBy());
        e.setUpdateBy(d.getUpdateBy());
        // ------------------------------------------------------------------
        // é—¨æ€¥è¯Šéšè®¿ä¸“用字段(OuthospSubtaskDraft)。
        //
        // âš ï¸ ç”¨ instanceof åˆ†æµï¼Œè€Œä¸æ˜¯ç»™ VisitSubtaskDraft åŠ å­—æ®µï¼š
        //    åŠ äº†å°±å¿…é¡»åœ¨è¿™é‡Œè¡¥æ˜ å°„ï¼Œè€Œè¡¥æ˜ å°„ä¼šè®©ã€å‡ºå…¥é™¢é‚£æ¡å·²éªŒæ”¶çš„è·¯å¾„ã€‘å¤šå†™åˆ—
        //    ï¼ˆinsertServiceSubtask æ˜¯ 39 ä¸ªæ¡ä»¶åˆ—,本来传 null çš„列从此会被写进去)。
        //    åˆ†æµåŽï¼šå‡ºå…¥é™¢ä¼ çš„æ˜¯ VisitSubtaskDraft,下面整块不执行 â€”— è¡Œä¸ºå®Œå…¨ä¸å˜ã€‚
        //    è§ OuthospSubtaskDraft çš„类注释。
        // ------------------------------------------------------------------
        if (d instanceof com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) {
            com.ruoyi.common.core.service.dto.OuthospSubtaskDraft o =
                    (com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) d;
            // åªæœ‰çˆ¶ç±»æ²¡æœ‰çš„字段才在这里补映射(其余如 sendname/sfzh/phone/sex/age çˆ¶ç±»å·²æœ‰ï¼‰
            e.setHospno(o.getHospno());
            e.setAdmindate(o.getAdmitdate());
            e.setFinishtime(o.getFinishtime());
            // âš ï¸ åŽŸå®žçŽ°ç¡¬ç¼–ç  "1"(不读 serviceTask.getHospType()),此处保持一致
            e.setHospType(o.getHospType());
        }
        return e;
    }
    private VisitPendingSubtaskDto toPendingDto(ServiceSubtask s) {
        VisitPendingSubtaskDto dto = new VisitPendingSubtaskDto();
        dto.setId(s.getId());
        dto.setTaskid(s.getTaskid());
        dto.setEndtime(s.getEndtime());
        dto.setLongSendTime(s.getLongSendTime());
        dto.setServiceType(s.getServiceType());
        dto.setOrgid(s.getOrgid());
        dto.setSendstate(s.getSendstate());
        // finishtime:门诊侧停旧子任务时要读到它吗?原实现是"在查询出来的完整对象上改",
        // è€Œå®ƒè¦æ”¹çš„就是 finishtime æœ¬èº«ï¼ˆè®¾ä¸º new Date()),所以这里带上查询值可保证
        // "未改动的字段回写等于不写"这一语义成立。
        dto.setFinishtime(s.getFinishtime());
        // result / remark / updateBy / updateTime åˆ»æ„ã€ä¸å¸¦è¿‡æ¥ã€‘:
        // å®ƒä»¬åŽŸæœ¬å°±æ˜¯ null(查询结果为 sendstate=2 çš„待执行记录),
        // ç”±è°ƒç”¨æ–¹åœ¨"移出"时按原实现设置。
        return dto;
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskArchivePort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package com.ruoyi.task.api.spi;
import com.ruoyi.common.core.domain.entity.PatArchive;
import java.util.List;
/**
 * æ‚£è€…档案读取能力 â€”— <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>为什么端口声明在 ruoyi-task、实现在 smartor</h3>
 * ä»»åŠ¡ä¾§è¦è¯» {@code pat_archive},但该表不归 ruoyi-task
 * ï¼ˆæ‚£è€…档案是独立域,本次只把 {@link PatArchive} è¿™ä¸ª<b>类型</b>上提到了 ruoyi-common)。
 * æŒ‰"各模块独占自己的数据访问"原则,ruoyi-task ä¸å¾—自写针对 {@code pat_archive} çš„ SQL。
 * <p>
 * åˆå› ä¸º ruoyi-task <b>不能依赖 smartor</b>(会形成
 * {@code smartor -> ruoyi-task -> smartor} ç¡¬å¾ªçŽ¯ï¼‰ï¼Œæ‰€ä»¥æ–¹å‘å¿…é¡»åè¿‡æ¥ï¼š
 * <b>接口声明在 ruoyi-task、实现放在 smartor</b>,由 Spring åœ¨è¿è¡ŒæœŸæ³¨å…¥ã€‚
 * ç¼–译期 ruoyi-task åªçœ‹åˆ°æŽ¥å£ï¼Œsmartor ä¾èµ– ruoyi-task å› æ­¤å¯ä»¥ implements å®ƒ â€”— æ— çŽ¯ã€‚
 *
 * <h3>为什么不需要新 DTO</h3>
 * {@link PatArchive} å·²ä¸Šæåˆ° {@code com.ruoyi.common.core.domain.entity},
 * åŒæ–¹éƒ½å¯è§ï¼Œå› æ­¤ç«¯å£ç›´æŽ¥ç”¨å®žä½“本身,<b>无需逐字段映射</b>。
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 * å®žçŽ°æ–¹åªåšè½¬å‘æˆ–ç­‰ä»·çš„ç±»åž‹è½¬æ¢ï¼Œä¸å¾—åŠ å…¥ä¸šåŠ¡åˆ¤æ–­ã€‚
 *
 * @see com.ruoyi.common.core.domain.entity.PatArchive
 */
public interface TaskArchivePort {
    /**
     * æŒ‰æ‚£è€…档案 id æŸ¥æ¡£æ¡ˆï¼ˆåŽŸ {@code PatArchiveMapper#selectPatArchiveByPatid},
     * äº¦å³ {@code IPatArchiveService#selectPatArchiveByPatid} â€”— åŽè€…是前者的纯委托)。
     *
     * @param patid æ¡£æ¡ˆ id
     * @return æ¡£æ¡ˆï¼›ä¸å­˜åœ¨è¿”回 {@code null}
     */
    PatArchive findByPatid(Long patid);
    /**
     * æŒ‰æ¡ä»¶æŸ¥æ¡£æ¡ˆåˆ—表(原 {@code PatArchiveMapper#selectPatArchiveList})。
     *
     * @param query æŸ¥è¯¢æ¡ä»¶ï¼ˆéž null å­—段参与过滤)
     * @return æ¡£æ¡ˆé›†åˆ
     */
    List<PatArchive> findList(PatArchive query);
    /**
     * æŒ‰ id æŸ¥ã€Œå¯¼å…¥æ¡£æ¡ˆã€å¹¶è½¬æˆæ¡£æ¡ˆè§†å›¾ï¼ˆåŽŸ
     * {@code DtoConversionUtils.sourceToTarget(patArchiveImportMapper.selectPatArchiveImportById(patid), PatArchive.class)})。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°æ‹¿åˆ°çš„ {@code PatArchiveImport} <b>从不逐字段访问</b>,而是整体转成
     * {@link PatArchive},所以这里把"取 + è½¬"合成一个方法,调用方拿到的仍是 {@link PatArchive},
     * <b>行为完全不变</b>,且无需把 {@code PatArchiveImport} æš´éœ²åˆ° ruoyi-task。
     *
     * @param patid æ¡£æ¡ˆ id
     * @return è½¬æ¢åŽçš„æ¡£æ¡ˆï¼›ä¸å­˜åœ¨è¿”回 {@code null}
     */
    PatArchive findImportedByPatid(Long patid);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskFtpPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.task.api.spi;
/**
 * FTP ä¸‹è½½èƒ½åŠ› â€”— <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>用途</h3>
 * å­ä»»åŠ¡ç»Ÿè®¡é‡Œæœ‰ä¸€æ®µ"把当天的录音目录从 FTP æ‹‰åˆ°æœ¬åœ°"的动作
 * ï¼ˆåŽŸ {@code ServiceSubtaskServiceImpl} L2859:
 * {@code ftpService.downloadFolder("/" + LocalDate.now(), profile + "/upload/vadio/voice/" + LocalDate.now())})。
 * {@code FtpService} æ˜¯ smartor çš„通用工具,不属任务域,因此经本端口取用。
 *
 * <h3>为什么端口在 ruoyi-task è€Œä¸åœ¨ ruoyi-common</h3>
 * æœ¬ç«¯å£åªè¢« ruoyi-task ä½¿ç”¨ã€ä¸”参数全是基本类型,没有"多方共享的契约类型",
 * æ”¾åœ¨ ruoyi-task çš„ {@code api.spi} ä¸‹å³å¯ï¼Œä¸å¿…污染 ruoyi-common。
 * æ–¹å‘仍是「接口在 ruoyi-task、实现在 smartor」,编译期无环。
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 */
public interface TaskFtpPort {
    /**
     * é€’归下载远端目录到本地(原 {@code FtpService#downloadFolder})。
     *
     * @param remoteDir è¿œç«¯ç›®å½•
     * @param localDir  æœ¬åœ°ç›®å½•
     */
    void downloadFolder(String remoteDir, String localDir);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskPreachformPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package com.ruoyi.task.api.spi;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import java.util.List;
/**
 * å­ä»»åŠ¡ã€Œæ‰§è¡Œæ–¹å¼æ˜Žç»†ã€ï¼ˆ{@code service_subtask_preachform})读写能力 â€”—
 * <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>为什么需要它</h3>
 * ä»»åŠ¡ä¾§çš„ 4 ä¸ªç±»ï¼ˆ{@code ServiceSubtaskServiceImpl} / {@code ServiceTaskServiceImpl} /
 * {@code ServiceTaskScheduleServiceImpl} / {@code ScheduleSubtaskBuilder})都要读写这张表,
 * ä½†è¯¥è¡¨çš„<b>归属模块是 {@code ruoyi-inhospvisit}</b>(已收口到 ruoyi-common çš„
 * {@code PreachformApi}),ruoyi-task ä¸èƒ½ä¾èµ– smartor、也不该自写该表 SQL。
 *
 * <h3>⚠️ è¿™æ˜¯ã€è¿‡æ¸¡ã€‘端口,形态刻意与旧 Mapper ä¸€è‡´</h3>
 * æœ¬ç«¯å£<b>逐方法镜像</b> smartor æ—¢æœ‰çš„(已 {@code @Deprecated} çš„)
 * {@code ServiceSubtaskPreachformMapper}:同名方法、同名参数、同返回类型
 * ï¼ˆ{@code insert/update} ä»è¿”回 {@code int})。
 * è¿™æ ·é‚£ 4 ä¸ªç±»æ¬è¿› ruoyi-task æ—¶<b>只需把注入的字段类型换成本端口,调用点一行都不用改</b>,
 * æŠŠ"改 25 å¤„调用点 + é€å¤„换类型"的风险降到最低。
 * <p>
 * ä¹‹æ‰€ä»¥ä¸ç›´æŽ¥è®©å®ƒä»¬æ³¨å…¥ {@code PreachformApi}:那需要把
 * {@code ServiceSubtaskPreachform} æ¢æˆ {@code VisitPreachformRow}、
 * å¹¶æŠŠ {@code int} è¿”回值改成 {@code boolean},共约 25 å¤„调用点,
 * å±žç‹¬ç«‹çš„æ”¶å£åŠ¨ä½œï¼ˆè§ {@code doc/跨模块数据访问原则与收口方案.md}),不在本次范围内。
 *
 * <h3>将来动作</h3>
 * å¾… preachform å…¨é¢æ”¶å£æ—¶ï¼ŒæŠŠæœ¬ç«¯å£ä¸Ž smartor çš„ Bridge ä¸€å¹¶åˆ é™¤ï¼Œ
 * 4 ä¸ªç±»æ”¹ä¸ºç›´æŽ¥æ³¨å…¥ {@code PreachformApi}。
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 * å®žçŽ°æ–¹åªè½¬å‘åˆ°æ—¢æœ‰ Mapper/Bridge,<b>不得</b>加入业务判断或字段补齐。
 *
 * @see com.ruoyi.common.core.service.PreachformApi è¯¥è¡¨çœŸæ­£çš„归属契约
 */
public interface TaskPreachformPort {
    /**
     * æŒ‰ä¸»é”®æŸ¥ï¼ˆåŽŸ {@code selectServiceSubtaskPreachformById})。
     */
    ServiceSubtaskPreachform selectServiceSubtaskPreachformById(Long id);
    /**
     * æŒ‰æ¡ä»¶æŸ¥åˆ—表(原 {@code selectServiceSubtaskPreachformList})。
     */
    List<ServiceSubtaskPreachform> selectServiceSubtaskPreachformList(ServiceSubtaskPreachform query);
    /**
     * æ–°å¢žï¼ˆåŽŸ {@code insertServiceSubtaskPreachform})。
     *
     * @return å—影响行数(与原实现一致,仍为 {@code int})
     */
    int insertServiceSubtaskPreachform(ServiceSubtaskPreachform row);
    /**
     * æŒ‰ä¸»é”®æ›´æ–°ï¼ˆåŽŸ {@code updateServiceSubtaskPreachform})。
     */
    int updateServiceSubtaskPreachform(ServiceSubtaskPreachform row);
    /**
     * æŒ‰æ¡ä»¶æ›´æ–°ï¼ˆåŽŸ {@code updateSSPByCondition})。
     */
    int updateSSPByCondition(ServiceSubtaskPreachform row);
    /**
     * æŒ‰ {@code subid} åˆ é™¤è¯¥å­ä»»åŠ¡çš„æ‰€æœ‰å‘é€æ–¹å¼ï¼ˆåŽŸ
     * {@code deleteServiceSubtaskPreachformBySubid},实际是软删)。
     */
    int deleteServiceSubtaskPreachformBySubid(Long subId);
    /**
     * æŒ‰ taskid/orgid æŸ¥ï¼ˆåŽŸ {@code selectBySubtask};⚠️ æ–¹æ³•名易误解,
     * å®ƒä¸æŒ‰ subtask ä¸»é”®æŸ¥ï¼Œè§ {@code PreachformApi#findByTaskidAndOrgid})。
     */
    List<ServiceSubtaskPreachform> selectBySubtask(ServiceSubtask subtask);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSubtaskRecordPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,47 @@
package com.ruoyi.task.api.spi;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
import java.util.List;
import java.util.Map;
/**
 * å­ä»»åŠ¡ã€Œå‘é€è®°å½•ã€ï¼ˆ{@code service_subtask_record})读取能力 â€”—
 * <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>为什么需要它</h3>
 * {@code service_subtask_record} è¿™å¼ è¡¨<b>不在本次模块化范围内</b>(它的 Mapper/XML/Service
 * ä»ç•™åœ¨ smartor),但它的<b>实体类型</b> {@link ServiceSubtaskRecord} å·²éš
 * {@code ServiceSubtask} çš„字段 `List&lt;ServiceSubtaskRecord&gt; serviceSubtaskRecordList`
 * ä¸€èµ·æ¬è¿›äº† ruoyi-task。
 * <p>
 * ä»»åŠ¡ä¾§ï¼ˆåŽŸ {@code ServiceSubtaskController} çš„ 2 å¤„调用)要读这张表,
 * æ•…经本端口取用 â€”— æ–¹å‘仍是「接口在 ruoyi-task、实现在 smartor」,编译期无环。
 *
 * <h3>⚠️ è¿™æ˜¯ã€è¿‡æ¸¡ã€‘端口,形态刻意与旧 Service ä¸€è‡´</h3>
 * é€æ–¹æ³•镜像 smartor çš„ {@code IServiceSubtaskRecordService} ä¸­ä»»åŠ¡ä¾§çœŸæ­£ç”¨åˆ°çš„é‚£ä¸¤ä¸ªæ–¹æ³•
 * ï¼ˆåŒåã€åŒå‚数、同返回类型),使 Controller æ¬è¿› ruoyi-task æ—¶
 * <b>只需换注入字段类型,调用点一行都不用改</b>。
 *
 * <h3>将来动作</h3>
 * è‹¥å°†æ¥ {@code service_subtask_record} ä¹Ÿçº³å…¥ä»»åŠ¡æ¨¡å—ï¼Œæœ¬ç«¯å£å³å¯åˆ é™¤ï¼Œ
 * æ”¹ä¸ºæ¨¡å—内直接调用。
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 */
public interface TaskSubtaskRecordPort {
    /**
     * æŒ‰æ¡ä»¶æŸ¥å‘送记录列表(原 {@code IServiceSubtaskRecordService#selectServiceSubtaskRecordList})。
     */
    List<ServiceSubtaskRecord> selectServiceSubtaskRecordList(ServiceSubtaskRecord query);
    /**
     * æŒ‰å­ä»»åŠ¡ id é›†åˆæ‰¹é‡æŸ¥å‘送记录,按 subid åˆ†ç»„
     * ï¼ˆåŽŸ {@code IServiceSubtaskRecordService#selectRecordMapBySubtaskIds})。
     *
     * @param subtaskIds å­ä»»åŠ¡ id é›†åˆ
     * @return subid -> è¯¥å­ä»»åŠ¡çš„è®°å½•åˆ—è¡¨
     */
    Map<Long, List<ServiceSubtaskRecord>> selectRecordMapBySubtaskIds(List<Long> subtaskIds);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSysConfigPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package com.ruoyi.task.api.spi;
/**
 * å‚数配置读取能力 â€”— <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>用途</h3>
 * å­ä»»åŠ¡ç»Ÿè®¡é‡Œè¦è¯» {@code sys_config} çš„ {@code wj.sms.template} å–值
 * ï¼ˆåŽŸ {@code ServiceSubtaskServiceImpl} L5141-5146):
 * <pre>
 * SysConfig sysConfig = new SysConfig();
 * sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template");
 * if (!Objects.isNull(sysConfig)) { String smsContemt = sysConfig.getConfigValue(); ... }
 * </pre>
 * {@code sys_config} å±žç³»ç»ŸåŸŸã€‚ruoyi-task è‡ªå·±çš„æ¨¡å—开关读参数走
 * {@code ruoyi-common} çš„ {@code IConfigService};但这里原本走的是
 * {@code SysConfigMapper#checkConfigKeyUnique}(返回整行),
 * ä¸”只用到 {@code configValue} ä¸€ä¸ªå­—段,故收成一个<b>只返回值</b>的端口。
 *
 * <h3>⚠️ å…³äºŽ"行存在但值为 null"的边界</h3>
 * åŽŸåˆ¤å®šæ˜¯"行存在"({@code !isNull(sysConfig)}),本端口判定是"值非 null"。
 * äºŒè€…只在 {@code sys_config} å­˜åœ¨ä¸€è¡Œä¸” {@code config_value IS NULL} æ—¶ä¸åŒ â€”—
 * è€Œ RuoYi çš„ {@code sys_config.config_value} æ˜¯ {@code varchar(500) NOT NULL DEFAULT ''},
 * <b>该情形不可能出现</b>,因此行为等价。
 * ï¼ˆå®žçŽ°æ–¹æŒ‰åŽŸæ ·è°ƒ {@code checkConfigKeyUnique} åŽå– {@code getConfigValue()}。)
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 */
public interface TaskSysConfigPort {
    /**
     * æŒ‰å‚数键名取参数值(原 {@code SysConfigMapper#checkConfigKeyUnique} + {@code getConfigValue()})。
     *
     * @param configKey å‚数键名
     * @return å‚数值;该键不存在时返回 {@code null}
     */
    String findConfigValueByKey(String configKey);
    /**
     * æŒ‰å‚数键名取参数值(原 {@code ISysConfigService#selectConfigByKey(String)})。
     * <p>
     * âš ï¸ ä¸Ž {@link #findConfigValueByKey} <b>不是同一个东西</b>,不要合并:
     * å‰è€…走的是 {@code SysConfigMapper#checkConfigKeyUnique}(按 key æŸ¥æ•´è¡Œï¼‰ï¼Œ
     * æœ¬æ–¹æ³•走的是 {@code ISysConfigService#selectConfigByKey}(带缓存、且支持 orgid ç»´åº¦ï¼‰ã€‚
     * ä¸¤è€… SQL ä¸åŒï¼Œåˆå¹¶ä¼šæ”¹å˜å–值来源 â€”— æ•…并列保留、各自对应原调用。
     *
     * @param configKey å‚数键名
     * @return å‚数值;不存在返回 {@code StringUtils.EMPTY}(与原实现一致,不是 null)
     */
    String selectConfigValue(String configKey);
    /**
     * æŒ‰å‚数键名 + æœºæž„ id å–参数值(原 {@code ISysConfigService#selectConfigByKey(String, String)})。
     * <p>
     * âš ï¸ è¯¥ 2 å‚重载<b>只在 ruoyi-system çš„ {@code ISysConfigService} ä¸Š</b>,
     * ä¸åœ¨ ruoyi-common çš„ {@code IConfigService} ä¸Š â€”— è¿™ä¹Ÿæ˜¯ä¸ºä»€ä¹ˆä¸èƒ½ç®€å•地把
     * è°ƒç”¨æ–¹å­—段类型换成 {@code IConfigService} æ¥çœæŽ‰ä¾èµ–。
     *
     * @param configKey å‚数键名
     * @param orgid     æœºæž„ id
     * @return å‚数值;不存在返回空串
     */
    String selectConfigValue(String configKey, String orgid);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSysDeptPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package com.ruoyi.task.api.spi;
import com.ruoyi.common.core.domain.entity.SysDept;
/**
 * ç§‘室查询能力 â€”— <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>用途</h3>
 * ä»»åŠ¡åˆ›å»º/子任务组装时按科室编码反查科室信息
 * ï¼ˆåŽŸ {@code SysDept2Mapper#selectDeptByCode(deptCode, orgid)},
 * è§ {@code ServiceSubtaskServiceImpl} L1004 / L5488)。
 * {@code sys_dept} å±žç³»ç»ŸåŸŸï¼Œruoyi-task ä¸å¾—自写 SQL,也不依赖 ruoyi-system,
 * å› æ­¤ç»æœ¬ç«¯å£å–用。
 *
 * <h3>为什么不需要新 DTO</h3>
 * è¿”回类型 {@link SysDept} æœ¬èº«å°±åœ¨ {@code ruoyi-common},双方可见,直接用实体。
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 * åªè½¬å‘到 {@code SysDept2Mapper},不得加入默认值补齐或兜底查询等新逻辑。
 */
public interface TaskSysDeptPort {
    /**
     * æŒ‰ç§‘室编码查科室(原 {@code SysDept2Mapper#selectDeptByCode})。
     *
     * @param deptCode ç§‘室编码
     * @param orgid    æœºæž„ id(可为 {@code null})
     * @return ç§‘室;不存在返回 {@code null}
     */
    SysDept selectDeptByCode(String deptCode, String orgid);
}
ruoyi-task/src/main/java/com/ruoyi/task/api/spi/TaskSysUserDeptPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package com.ruoyi.task.api.spi;
import com.ruoyi.common.core.domain.entity.SysUserDept;
import java.util.List;
/**
 * ã€Œç”¨æˆ·-科室/病区」关系查询能力 â€”— <b>ruoyi-task çš„对外 SPI,由 smartor ä¾§å®žçް</b>。
 *
 * <h3>用途</h3>
 * å­ä»»åŠ¡ç»Ÿè®¡ï¼ˆåŽŸ {@code ServiceSubtaskController} L610 / L625 / L903 / L909)要按当前登录人
 * æŸ¥å‡ºå…¶ç§‘室、病区列表,用于"按科室/病区分组"的统计过滤。
 * {@code sys_user_dept} å±žç³»ç»ŸåŸŸï¼Œruoyi-task <b>不得依赖 ruoyi-system</b>,故经本端口取用。
 *
 * <h3>为什么不需要新 DTO</h3>
 * å‚数与返回类型 {@link SysUserDept} æœ¬å°±åœ¨ {@code ruoyi-common},双方可见。
 *
 * <h3>⚠️ å®žçŽ°å¿…é¡»ä¿æŒ"纯转发"</h3>
 * å®žçŽ°æ–¹åªè½¬å‘åˆ° {@code ISysUserDeptService#selectSysUserDeptList},不加任何兜底/默认值逻辑。
 */
public interface TaskSysUserDeptPort {
    /**
     * æŒ‰æ¡ä»¶æŸ¥ç”¨æˆ·-科室关系(原 {@code ISysUserDeptService#selectSysUserDeptList})。
     *
     * @param query æŸ¥è¯¢æ¡ä»¶ï¼ˆéž null å­—段参与过滤)
     * @return å…³ç³»åˆ—表
     */
    List<SysUserDept> selectSysUserDeptList(SysUserDept query);
}
ruoyi-task/src/main/java/com/ruoyi/task/common/ScheduleSubtaskBuilder.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/common/ScheduleSubtaskBuilder.java ÐÞ¸Ä
@@ -1,11 +1,8 @@
package com.smartor.common;
package com.ruoyi.task.common;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.*;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.smartor.mapper.ServiceSubtaskPreachformMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
@@ -16,6 +13,14 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.api.spi.TaskPreachformPort;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * ä»»åŠ¡ç»„ï¼ˆappltype=5)subtask ç”Ÿæˆå™¨
@@ -37,7 +42,7 @@
    private ServiceSubtaskMapper serviceSubtaskMapper;
    @Autowired
    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
    private TaskPreachformPort taskPreachformPort;
    /**
     * ä¸ºå•个患者派生 subtask(appltype=5)—— è§£è€¦å…¥å£ï¼ˆè‡ªåŠ¨çº³å…¥ & æ‰‹åŠ¨å‹¾é€‰ç»Ÿä¸€èµ°è¿™é‡Œï¼‰ã€‚
@@ -174,7 +179,7 @@
            pf.setSendstate("1");
            pf.setOrgid(serviceTask.getOrgid());
            pf.setCreateTime(new Date());
            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(pf);
            taskPreachformPort.insertServiceSubtaskPreachform(pf);
        }
    }
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskAvailabilityInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.task.config;
import com.ruoyi.common.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
 * ä»»åŠ¡æ¨¡å—å¼€å…³æ‹¦æˆªå™¨ã€‚
 * <p>
 * <b>作用</b>:当开关关闭({@code sys_config} ä¸­ {@code task.enabled=false})时,
 * å‘½ä¸­ä»»åŠ¡ç«¯ç‚¹çš„è¯·æ±‚ä¼šè¢«ç«‹å³æ‹¦ä¸‹å¹¶æŠ›å‡º {@link ServiceException},
 * ç”± RuoYi çš„全局异常处理器返回<b>明确的业务错误提示</b>(而不是 404 æˆ–静默返回空数据)。
 * <p>
 * å¼€å…³å€¼ç”± {@link TaskSwitch} åœ¨è¿è¡Œæ—¶è¯»å–({@code sys_config},10 ç§’进程内缓存),
 * å› æ­¤æ”¹å®Œå‚数设置<b>不需要重启</b>,最多 10 ç§’即生效。
 * <p>
 * <b>为什么不用 {@code @ConditionalOnProperty} è®© Controller ç›´æŽ¥ä¸æ³¨å†Œ</b>:
 * <ul>
 *   <li>那样调用方只会拿到 HTTP 404 â€”— è™½ç„¶ä¹Ÿæ˜¯å¤±è´¥ï¼Œä½†ä¸æ˜¯"明确错误",
 *       å‰ç«¯/外链无法区分"模块被关闭"和"地址写错了";</li>
 *   <li>而且 {@code @ConditionalOnProperty} åªåœ¨<b>启动时</b>求值一次,
 *       åšä¸åˆ°"改完参数配置就生效",与参数表开关的诉求冲突。</li>
 * </ul>
 * <p>
 * <b>为什么不会影响其它业务与启动</b>:本拦截器只登记在任务模块的
 * 2 ä¸ª URL å‰ç¼€ä¸Šï¼ˆè§ {@link TaskWebConfig}),不会碰到随访/问卷/其它接口。
 */
@Slf4j
@Component
public class TaskAvailabilityInterceptor implements HandlerInterceptor {
    /**
     * æ¨¡å—被关闭时返回给调用方的错误提示(前端可直接展示)
     */
    public static final String DISABLED_MESSAGE =
            "任务功能未启用(参数配置 task.enabled=false),该接口不可用";
    private final TaskSwitch taskSwitch;
    public TaskAvailabilityInterceptor(TaskSwitch taskSwitch) {
        this.taskSwitch = taskSwitch;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!taskSwitch.isEnabled()) {
            // request å…è®¸ä¸º null(单元测试直接调 preHandle æ—¶ä¸æž„造请求对象)
            String method = request == null ? "-" : request.getMethod();
            String uri = request == null ? "-" : request.getRequestURI();
            log.warn("【任务】模块已关闭(参数配置 task.enabled=false),拒绝访问 {} {}", method, uri);
            throw new ServiceException(DISABLED_MESSAGE);
        }
        return true;
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.task.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * ä»»åŠ¡æ¨¡å—é…ç½®ï¼ˆå‰ç¼€ {@code task})。
 * <p>
 * âš ï¸ <b>这不是主开关</b>。任务模块的开关<b>存在 {@code sys_config} å‚数配置表里</b>:
 * <pre>
 * å‚数键名:task.enabled
 * å‚数键值:true(启用,默认)/ false(关闭)
 * </pre>
 * è§ {@link TaskSwitch}。本类的 {@link #isEnabled()} åªä½œä¸º<b>兜底</b>:
 * å½“ {@code sys_config} é‡Œæ²¡æœ‰è¿™ä¸€è¡Œã€æˆ–非 Spring çŽ¯å¢ƒå–ä¸åˆ°é…ç½®æœåŠ¡æ—¶æ‰ä¼šç”¨åˆ°ã€‚
 * <p>
 * å¦‚需在 yml ä¸­å…œåº•(一般不必要,正常应在参数设置页维护):
 * <pre>
 * task:
 *   enabled: false
 * </pre>
 */
@Data
@Component
@ConfigurationProperties(prefix = "task")
public class TaskProperties {
    /**
     * æ˜¯å¦å¯ç”¨ä»»åŠ¡æ¨¡å—ï¼ˆé»˜è®¤å¯ç”¨ï¼‰ã€‚
     * <p>
     * ä»…当 {@code sys_config} ä¸­æ²¡æœ‰ {@code task.enabled} æ—¶ç”Ÿæ•ˆï¼Œè§ {@link TaskSwitch}。
     */
    private boolean enabled = true;
}
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskSwitch.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,207 @@
package com.ruoyi.task.config;
import com.ruoyi.common.core.service.IConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * ä»»åŠ¡æ¨¡å—<b>总开关</b>:值存放在 {@code sys_config} å‚数配置表中,<b>运行时读取、可热切换</b>。
 *
 * <h3>参数配置</h3>
 * <pre>
 * å‚数键名:task.enabled
 * å‚数键值:true  = å¯ç”¨ï¼ˆé»˜è®¤ï¼‰
 *          false = å…³é—­
 * </pre>
 * ä½ç½®ï¼šç³»ç»Ÿç®¡ç† â†’ å‚数设置 â†’ æ–°å¢ž/修改。
 * <pre>
 * insert into sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
 * values ('任务模块开关', 'task.enabled', 'true', 'Y', 'admin', sysdate(), 'true启用,false关闭(默认true)');
 * </pre>
 *
 * <h3>取值优先级</h3>
 * <ol>
 *   <li>{@code sys_config} é‡Œ {@code task.enabled} çš„值(<b>主用</b>,
 *       æ¥è‡ª {@code IConfigService} æŽ¥å£ï¼Œå®žçŽ°åœ¨ ruoyi-system çš„ {@code SysConfigServiceImpl});</li>
 *   <li>{@link TaskProperties#isEnabled()}(<b>兜底</b>:仅当 sys_config é‡Œæ²¡æœ‰è¿™ä¸€è¡Œã€
 *       æˆ–非 Spring çŽ¯å¢ƒå–ä¸åˆ°é…ç½®æœåŠ¡æ—¶ä½¿ç”¨ï¼Œé»˜è®¤ true)。</li>
 * </ol>
 *
 * <h3>为什么默认 true</h3>
 * ä¸Žæ»¡æ„åº¦æ¨¡å—同款:参数行缺失时按 true å¤„理,保证升级后任务功能不会因为少了一行配置而突然不可用
 * ï¼ˆè¦å…³é—­å¿…须显式配置 false)。
 *
 * <h3>为什么读不到配置时也按 true(fail-open)</h3>
 * è¯» {@code sys_config} éœ€è¦èµ°æ•°æ®åº“,数据库抖动时如果 fail-closed ä¼šæŠŠæ•´ä¸ªä»»åŠ¡ä¸šåŠ¡æ‰“æ­»ã€‚
 * è¿™é‡Œåªæ‰“ WARN å¹¶æ”¾è¡Œï¼Œå®å¯å¤šå¼€ä¸€ä¼šå„¿ï¼Œä¹Ÿä¸åšæ•°æ®åº“抖动放大器。
 *
 * <h3>为什么有 10 ç§’缓存</h3>
 * {@code SysConfigServiceImpl#selectConfigByKey} å½“前<b>每次都查库</b>(Redis ç¼“存那段被注释掉了),
 * è€Œæœ¬å¼€å…³åœ¨æ¯æ¬¡ä»»åŠ¡è¯·æ±‚ã€æ¯æ¬¡å®ˆå«åˆ¤æ–­æ—¶éƒ½ä¼šè¢«è¯»åˆ°ã€‚è¿™é‡ŒåŠ ä¸€ä¸ªè¿›ç¨‹å†… TTL ç¼“存,
 * æŠŠæŸ¥åº“降到"每个实例每 10 ç§’最多一次"。代价是参数改动最多 10 ç§’生效(多实例部署时各自计时)。
 */
@Slf4j
@Component
public class TaskSwitch {
    /**
     * sys_config é‡Œçš„参数键名
     */
    public static final String CONFIG_KEY = "task.enabled";
    /**
     * è¿›ç¨‹å†…缓存有效期(毫秒)
     */
    private static final long CACHE_TTL_MILLIS = 10_000L;
    private final TaskProperties taskProperties;
    /**
     * å‚数配置服务(ruoyi-common çš„æŽ¥å£ï¼Œå®žçŽ°åœ¨ ruoyi-system)。
     * <p>
     * ç”¨ {@code ObjectProvider} æƒ°æ€§å–:本模块只依赖 ruoyi-common,
     * åœ¨å•元测试/独立部署等"容器里没有实现"的场景下不能因此启动失败。
     */
    private final ObjectProvider<IConfigService> configServiceProvider;
    /**
     * æœ€è¿‘一次解析结果
     */
    private volatile Boolean cachedEnabled;
    /**
     * æœ€è¿‘一次解析的时间戳
     */
    private volatile long cachedAt;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器
     * ï¼ˆå¦ä¸€ä¸ªç»™æµ‹è¯•/非 Spring çŽ¯å¢ƒç”¨ï¼‰ï¼Œä¸æ ‡æ³¨çš„è¯ Spring ä¼šå› "构造器不唯一"
     * é€€å›žæŸ¥æ‰¾æ— å‚构造器,启动即报 {@code No default constructor found}。
     * è¯¥è§„则已由 {@code sql/_probe/verify_spring_beans.py} é™æ€æŠŠå…³ã€‚
     */
    @Autowired
    public TaskSwitch(TaskProperties taskProperties,
                      ObjectProvider<IConfigService> configServiceProvider) {
        this.taskProperties = taskProperties;
        this.configServiceProvider = configServiceProvider;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造:{@code configValue} å°±æ˜¯ sys_config é‡Œçš„键值,
     * ä¼  {@code null} è¡¨ç¤º"参数行不存在"(此时回落到 {@link TaskProperties})。
     */
    public TaskSwitch(TaskProperties taskProperties, final String configValue) {
        this(taskProperties, new FixedProvider(configValue));
        // éž Spring çŽ¯å¢ƒä¸‹æ²¡æœ‰"改配置后等 10 ç§’"的概念,直接禁用缓存,保证断言即时可见
        this.cachedEnabled = resolve();
        this.cachedAt = System.currentTimeMillis() + CACHE_TTL_MILLIS * 1000L;
    }
    /**
     * ä»»åŠ¡æ¨¡å—æ˜¯å¦å¯ç”¨ï¼ˆè¿è¡Œæ—¶çƒ­è¯»ï¼Œæœ€å¤š 10 ç§’延迟)
     */
    public boolean isEnabled() {
        long now = System.currentTimeMillis();
        Boolean cached = this.cachedEnabled;
        if (cached != null && now - this.cachedAt < CACHE_TTL_MILLIS) {
            return cached;
        }
        boolean resolved = resolve();
        this.cachedEnabled = resolved;
        this.cachedAt = now;
        return resolved;
    }
    /**
     * æ¸…掉进程内缓存,下次调用立即重新读 sys_config(供测试/需要立即生效的场景)
     */
    public void invalidateCache() {
        this.cachedEnabled = null;
    }
    /**
     * è§£æžå½“前开关值:sys_config ä¼˜å…ˆï¼Œå…¶æ¬¡ yml å…œåº•
     */
    private boolean resolve() {
        String value = readFromSysConfig();
        if (value != null) {
            if (!"true".equalsIgnoreCase(value) && !"false".equalsIgnoreCase(value)) {
                log.warn("【任务】sys_config ä¸­ {} çš„值不是 true/false(实际=「{}」),将按「不是 false å³å¯ç”¨ã€å¤„理",
                        CONFIG_KEY, value);
            }
            boolean enabled = !"false".equalsIgnoreCase(value);
            log.debug("【任务】开关取自 sys_config:{}={} -> enabled={}", CONFIG_KEY, value, enabled);
            return enabled;
        }
        boolean enabled = taskProperties.isEnabled();
        log.debug("【任务】sys_config ä¸­æœªé…ç½® {},回落 task.enabled={}", CONFIG_KEY, enabled);
        return enabled;
    }
    /**
     * è¯» sys_config çš„原始键值;没有实现、没有这一行、或读库失败都返回 {@code null}
     */
    private String readFromSysConfig() {
        if (configServiceProvider == null) {
            return null;
        }
        try {
            IConfigService configService = configServiceProvider.stream().findFirst().orElse(null);
            if (configService == null) {
                return null;
            }
            String value = configService.selectConfigByKey(CONFIG_KEY);
            if (value == null || value.trim().isEmpty()) {
                return null;
            }
            return value.trim();
        } catch (Exception e) {
            // å®¹å™¨æ²¡æœ‰å®žçŽ°ã€æ•°æ®æºå¼‚å¸¸ã€SQL æŠ¥é”™â€¦â€¦ä¸€å¾‹æŒ‰"参数缺失"处理(fail-open),绝不影响主流程
            log.warn("【任务】读取 sys_config çš„ {} å¤±è´¥ï¼Œæœ¬æ¬¡æŒ‰æœªé…ç½®å¤„理(fail-open):{}",
                    CONFIG_KEY, e.toString());
            return null;
        }
    }
    /**
     * å›ºå®šå€¼çš„ {@code ObjectProvider}:只用于非 Spring çŽ¯å¢ƒ/测试
     */
    private static final class FixedProvider implements ObjectProvider<IConfigService> {
        private final IConfigService configService;
        private FixedProvider(final String configValue) {
            this.configService = configValue == null ? null : configKey -> configValue;
        }
        @Override
        public IConfigService getObject(Object... args) {
            return configService;
        }
        @Override
        public IConfigService getIfAvailable() {
            return configService;
        }
        @Override
        public IConfigService getIfUnique() {
            return configService;
        }
        @Override
        public IConfigService getObject() {
            return configService;
        }
        @Override
        public java.util.stream.Stream<IConfigService> stream() {
            return configService == null ? java.util.stream.Stream.empty() : java.util.stream.Stream.of(configService);
        }
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/config/TaskWebConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package com.ruoyi.task.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.ArrayList;
import java.util.List;
/**
 * ä»»åŠ¡æ¨¡å—çš„ MVC é…ç½®ï¼šæŠŠ {@link TaskAvailabilityInterceptor} æŒ‚到任务模块自己的端点前缀上。
 *
 * <h3>为什么是 2 ä¸ªå‰ç¼€ï¼ˆè€Œä¸æ˜¯é€ä¸ªæžšä¸¾ 42 ä¸ªç«¯ç‚¹ï¼‰</h3>
 * ä»»åŠ¡æ¨¡å—çš„ç«¯ç‚¹**全部**由本模块的 2 ä¸ª Controller æä¾›ï¼Œä¸”它们的 URL å‰ç¼€
 * ä¸Žå…¶ä»–模块**没有任何重叠**:
 * <ul>
 *   <li>{@code /smartor/servicetask/**} â€”— 11 ä¸ªç«¯ç‚¹ï¼›</li>
 *   <li>{@code /smartor/serviceSubtask/**} â€”— 42 ä¸ªç«¯ç‚¹ã€‚</li>
 * </ul>
 * âš ï¸ ç‰¹åˆ«æ³¨æ„ {@code /smartor/serviceSubtask/**} <b>不会</b>误伤满意度模块的
 * {@code /smartor/serviceSubtaskDetail/**}:Spring çš„路径匹配里
 * {@code serviceSubtask} ä¸Ž {@code serviceSubtaskDetail} æ˜¯<b>两个不同的段</b>。
 * <p>
 * å› æ­¤è¿™é‡Œç”¨å‰ç¼€å³å¯ç²¾ç¡®è¦†ç›–,无需像满意度模块那样逐个列 17 ä¸ªç²¾ç¡®è·¯å¾„。
 * å”¯ä¸€éœ€è¦é¢å¤–处理的是"电话模块保留原 URL åŽè½åœ¨è¿™ä¸ªå‰ç¼€é‡Œçš„ 6 æ¡ç«¯ç‚¹",
 * è§ {@link #excludedPhonePaths()}。
 * è‹¥å°†æ¥åˆæœ‰å…¶å®ƒæ¨¡å—往这两个前缀下加端点,同样需要加到排除清单里。
 */
@Configuration
public class TaskWebConfig implements WebMvcConfigurer {
    /**
     * ä»»åŠ¡ï¼ˆåŽŸ {@code ServiceTaskController})
     */
    private static final String SERVICETASK_PATTERN = "/smartor/servicetask/**";
    /**
     * å­ä»»åŠ¡ï¼ˆåŽŸ {@code ServiceSubtaskController})
     */
    private static final String SERVICESUBTASK_PATTERN = "/smartor/serviceSubtask/**";
    /**
     * æŒ‚在 {@code /smartor/serviceSubtask/**} ä¸‹ã€ä½†<b>归电话模块(ruoyi-phone)</b>的 6 æ¡ç«¯ç‚¹ã€‚
     * <p>
     * ç”µè¯æ¨¡å—化时<b>刻意保留原 URL</b>(外部电话平台的回调地址不能改),于是这几条
     * è½åœ¨ä»»åŠ¡æ¨¡å—çš„å‰ç¼€é‡Œã€‚å®ƒä»¬å¿…é¡»ä»Ž"任务开关"的拦截范围里排除,
     * å¦åˆ™ä¼šå‡ºçް"关掉 task.enabled é¡ºæ‰‹æŠŠç”µè¯å›žè°ƒä¹Ÿå…³äº†"——外部平台正在通话,
     * å›žè°ƒè¢«æ‹¦ä¼šç›´æŽ¥æŠŠéšè®¿æ‰“断。它们由 {@code phone.enabled} ç®¡ã€‚
     * <p>
     * âš ï¸ è¿™é‡Œæ˜¯<b>字符串字面量</b>而不是引用 {@code PhoneWebConfig} çš„常量:
     * ä¾èµ–方向只能是 {@code ruoyi-phone -> ruoyi-task},反过来会形成 Maven å¾ªçŽ¯ä¾èµ–ã€‚
     * æ”¹ç”µè¯ç«¯ç‚¹è·¯å¾„时两边都要改;{@code TaskSwitchCheck} é‡Œæœ‰æ–­è¨€ç›¯ç€è¿™ 6 æ¡ã€‚
     */
    private static final String[] PHONE_OWNED_SUBTASK_PATHS = new String[]{
            "/smartor/serviceSubtask/phoneCallBack",
            "/smartor/serviceSubtask/phoneCallBackYQ",
            "/smartor/serviceSubtask/taskPull",
            "/smartor/serviceSubtask/saveQuestionAnswerPhone",
            "/smartor/serviceSubtask/recordAcceptshanghai",
            "/smartor/serviceSubtask/recordAccept",
    };
    @Autowired
    private TaskAvailabilityInterceptor taskAvailabilityInterceptor;
    /**
     * ä¾›æµ‹è¯•/非 Spring çŽ¯å¢ƒç›´æŽ¥æž„é€ 
     */
    public TaskWebConfig() {
    }
    public TaskWebConfig(TaskAvailabilityInterceptor interceptor) {
        this.taskAvailabilityInterceptor = interceptor;
    }
    /**
     * æœ¬æ¨¡å—需要"关闭即明确报错"的 URL å‰ç¼€ï¼ˆä¾›æ ¡éªŒè„šæœ¬/单元测试断言)
     */
    public static List<String> guardedPathPatterns() {
        List<String> patterns = new ArrayList<>();
        patterns.add(SERVICETASK_PATTERN);
        patterns.add(SERVICESUBTASK_PATTERN);
        return patterns;
    }
    /**
     * ä»Žä»»åŠ¡å¼€å…³æ‹¦æˆªèŒƒå›´é‡ŒæŽ’é™¤çš„è·¯å¾„ï¼ˆç”µè¯æ¨¡å—è‡ªå·±çš„ 6 æ¡ï¼Œä¾›æ ¡éªŒè„šæœ¬/单元测试断言)
     */
    public static List<String> excludedPhonePaths() {
        List<String> paths = new ArrayList<>();
        for (String p : PHONE_OWNED_SUBTASK_PATHS) {
            paths.add(p);
        }
        return paths;
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(taskAvailabilityInterceptor)
                .addPathPatterns(guardedPathPatterns())
                .excludePathPatterns(PHONE_OWNED_SUBTASK_PATHS)
                // æ‹¦æˆªå™¨æœ¬èº«å¾ˆè½»ï¼ˆåªè¯»ä¸€ä¸ª boolean),order ç»™ä¸ªå°å€¼è®©å®ƒåœ¨ä¸šåŠ¡æ‹¦æˆªå™¨ä¹‹å‰æ‰§è¡Œ
                .order(-100);
    }
}
ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceSubtaskController.java
ÎļþÃû´Ó ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceSubtaskController.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.ruoyi.web.controller.smartor;
package com.ruoyi.task.controller;
import com.alibaba.fastjson.JSON;
import com.github.pagehelper.ISelect;
@@ -19,18 +19,12 @@
import com.ruoyi.common.utils.*;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.utils.reflect.ReflectUtils;
import com.ruoyi.quartz.service.ICollectHISService;
import com.ruoyi.quartz.task.RyTask;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysUserDeptService;
import com.ruoyi.common.core.service.TaskOpsTriggerApi;
import com.ruoyi.task.api.spi.TaskSysConfigPort;
import com.ruoyi.task.api.spi.TaskSysUserDeptPort;
import com.ruoyi.satisfaction.api.SatisfactionExportBundle;
import com.ruoyi.satisfaction.api.SatisfactionFacade;
import com.smartor.domain.*;
import com.smartor.domain.VO.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskRecordService;
import com.smartor.service.IServiceSubtaskService;
import com.smartor.service.IServiceTaskService;
import com.ruoyi.task.api.spi.TaskSubtaskRecordPort;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -51,9 +45,35 @@
import com.ruoyi.template.domain.VO.HeLibraryCountVO;
import static com.ruoyi.common.utils.SecurityUtils.getLoginUser;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceStatisticsRequest;
import com.ruoyi.task.domain.ServiceStatisticsResponse;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
import com.ruoyi.task.domain.ServiceSubtaskStatistic;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.VO.ServiceSubtaskCotinueCountVO;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.ruoyi.task.service.IServiceTaskService;
import com.ruoyi.task.domain.PatArchiveSrmVO;
import com.ruoyi.task.domain.ServiceSubTaskDetailReq;
import com.ruoyi.task.domain.ServiceSubtaskDiagname;
import com.ruoyi.task.domain.ServiceSubtaskRes;
import com.ruoyi.task.domain.ServiceSubtaskTemplateVO;
import com.ruoyi.task.domain.ServiceTaskVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskVO;
import com.ruoyi.task.domain.VO.TransferDeptVO;
import com.ruoyi.task.domain.ServiceSubtaskExport;
/**
 * å•一任务(随访)Controller
 * <p>
 * âš ï¸ ç”µè¯ç›¸å…³çš„ 6 ä¸ªç«¯ç‚¹ï¼ˆphoneCallBack / phoneCallBackYQ / taskPull /
 * saveQuestionAnswerPhone / recordAcceptshanghai / recordAccept)已随电话模块化
 * æ•´ä½“搬进 {@code ruoyi-phone} çš„ {@code PhoneCallbackController},<b>URL ä¿æŒä¸å˜</b>
 * ï¼ˆå¤–部电话平台的回调地址不能改),因此本 Controller ä¸Žç”µè¯æ¨¡å—共用
 * {@code /smartor/serviceSubtask} è¿™ä¸€å‰ç¼€ï¼Œåªè¦å®Œæ•´è·¯å¾„不重复即可正常注册。
 *
 * @author ruoyi
 * @date 2024-02-02
@@ -84,19 +104,28 @@
    private IServiceTaskService serviceTaskService;
    @Autowired
    private ISysUserDeptService sysUserDeptService;
    private TaskSysUserDeptPort taskSysUserDeptPort;
    @Autowired
    private IServiceSubtaskRecordService serviceSubtaskRecordService;
    private TaskSubtaskRecordPort serviceSubtaskRecordService;
    @Autowired
    private ISysConfigService configService;
    private TaskSysConfigPort taskSysConfigPort;
    /**
     * ä»»åŠ¡è¿ç»´è§¦å‘èƒ½åŠ›ã€‚
     * <p>
     * åŽŸå…ˆæ­¤å¤„ç›´æŽ¥æ³¨å…¥ {@code ruoyi-quartz} çš„ {@code RyTask} / {@code ICollectHISService},
     * ä¼šè®©ã€Œç«¯ç‚¹å½’属」与「HIS é‡‡é›†å®žçŽ°ã€ç»‘æ­»ï¼šè¿™ 6 ä¸ªç«¯ç‚¹çš„归属要搬进 {@code ruoyi-task},
     * è€Œå®žçŽ°ï¼ˆ{@code RyTask} 2214 è¡Œã€æ³¨å…¥çº¦ 20 ä¸ª smartor service)不能搬,
     * å¦åˆ™å½¢æˆ {@code ruoyi-task -> smartor} + {@code ruoyi-quartz -> ruoyi-task} çš„硬循环。
     * <p>
     * æ”¹ä¸ºç» {@code ruoyi-common} çš„端口取用:接口在 ruoyi-common,
     * å®žçŽ°ï¼ˆ{@code TaskOpsTriggerApiImpl})留在 ruoyi-quartz。
     * è¿™æ ·ç«¯ç‚¹å¯ä»¥æ•´ä½“搬走而<b>不需要</b>依赖 ruoyi-quartz。
     */
    @Autowired
    private RyTask ryTask;
    @Autowired
    private ICollectHISService collectHISService;
    private TaskOpsTriggerApi taskOpsTriggerApi;
    @Value("${spring.profiles.active}")
    private String active;
@@ -478,69 +507,6 @@
    }
    /**
     * ç”µè¯å›žè°ƒä»»åŠ¡(上海)
     */
    @ApiOperation("电话回调任务")
    @PostMapping("/phoneCallBack")
    public AjaxResult phoneCallBack(@RequestBody PhoneCallBackVO phoneCallBackVO) {
        serviceSubtaskService.phoneCallBack(phoneCallBackVO);
        return success();
    }
    @ApiOperation("电话ASR通话回调(雨绮)")
    @PostMapping("/phoneCallBackYQ")
    public PhoneCallBackYQVO phoneCallBackYQ(@RequestBody PhoneCallReqYQVO phoneCallReqYQVO) {
        log.info("电话ASR通话回调(雨绮) ï¼Ÿ{}", phoneCallReqYQVO);
        PhoneCallBackYQVO phoneCallBackYQVO = serviceSubtaskService.phoneCallBackYQ(phoneCallReqYQVO);
        return phoneCallBackYQVO;
    }
    /**
     * ç”µè¯ä»»åŠ¡æ‹‰å–(雨绮)
     */
    @ApiOperation("电话任务拉取(雨绮)")
    @PostMapping("/taskPull")
    public List<PullTaskVO> taskPull() {
        log.info("电话任务拉取(雨绮)");
        List<PullTaskVO> pullTaskVOList = serviceSubtaskService.taskPull();
        log.info("电话任务拉取数据(雨绮):{}", pullTaskVOList);
        return pullTaskVOList;
    }
    /**
     * æ‚£è€…问题结果记录
     */
    @ApiOperation("患者问题结果记录(电话)")
    @PostMapping("/saveQuestionAnswerPhone")
    public AjaxResult saveQuestionAnswerPhone(@RequestBody ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
//        SysUser user = getLoginUser().getUser();
//        serviceSubTaskDetailReq.setOrgid(user.getOrgid());
        log.info("患者问题结果记录(电话):{}", serviceSubTaskDetailReq);
        return toAjax(serviceSubtaskService.saveQuestionAnswerPhone(serviceSubTaskDetailReq));
    }
    /**
     * é€šè¯è®°å½•回调
     */
    @ApiOperation("通话记录回调(上海)")
    @PostMapping("/recordAcceptshanghai")
    public AjaxResult recordAccepshanghait(@RequestBody ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
        log.info("通话记录回调(上海):{}", serviceSubTaskDetailReq);
        return toAjax(serviceSubtaskService.saveQuestionAnswerPhone(serviceSubTaskDetailReq));
    }
    /**
     * é€šè¯è®°å½•回调
     */
    @ApiOperation("通话记录回调(雨绮)")
    @PostMapping("/recordAccept")
    public AjaxResult recordAccept(@RequestBody PhoneCallRecordVO phoneCallRecordVO) {
        log.info("通话记录回调(雨绮):{}", phoneCallRecordVO);
        serviceSubtaskService.recordAccept(phoneCallRecordVO);
        return success();
    }
    /**
     * èŽ·å–æ¯ä¸ªæœˆçš„éšè®¿ã€å¤è¯Šé‡
     */
    @ApiOperation("获取每个月的随访、复诊量")
@@ -577,7 +543,7 @@
                        sysUserWard.setOrgid(user.getOrgid());
                        sysUserWard.setCampusid(user.getCampusid());
                        sysUserWard.setDeptType("2");
                        List<SysUserDept> wardList = sysUserDeptService.selectSysUserDeptList(sysUserWard);
                        List<SysUserDept> wardList = taskSysUserDeptPort.selectSysUserDeptList(sysUserWard);
                        if (CollectionUtils.isNotEmpty(wardList)) {
                            leavehospitaldistrictcodes = wardList.stream().map(SysUserDept::getDeptCode).collect(Collectors.toList());
                        }
@@ -592,7 +558,7 @@
                        sysUserDept.setOrgid(user.getOrgid());
                        sysUserDept.setCampusid(user.getCampusid());
                        sysUserDept.setDeptType("1");
                        List<SysUserDept> deptList = sysUserDeptService.selectSysUserDeptList(sysUserDept);
                        List<SysUserDept> deptList = taskSysUserDeptPort.selectSysUserDeptList(sysUserDept);
                        if (CollectionUtils.isNotEmpty(deptList)) {
                            deptCodes = deptList.stream().map(SysUserDept::getDeptCode).collect(Collectors.toList());
                        }
@@ -602,7 +568,7 @@
            }
        }
        String followUpCountStyle = configService.selectConfigByKey("followUpCountStyle", serviceSubtaskCountReq.getOrgid());
        String followUpCountStyle = taskSysConfigPort.selectConfigValue("followUpCountStyle", serviceSubtaskCountReq.getOrgid());
        if (ObjectUtils.isNotEmpty(followUpCountStyle)) {
            serviceSubtaskCountReq.setFollowUpCountStyle(followUpCountStyle);
        } else {
@@ -627,7 +593,7 @@
        Integer offset = PageUtils.getOffset(serviceSubtaskCountReq.getPageNum(), serviceSubtaskCountReq.getPageSize());
        serviceSubtaskCountReq.setPageNum(offset);
        String followUpCountStyle = configService.selectConfigByKey("followUpCountStyle", serviceSubtaskCountReq.getOrgid());
        String followUpCountStyle = taskSysConfigPort.selectConfigValue("followUpCountStyle", serviceSubtaskCountReq.getOrgid());
        if (ObjectUtils.isNotEmpty(followUpCountStyle)) {
            serviceSubtaskCountReq.setFollowUpCountStyle(followUpCountStyle);
        } else {
@@ -739,7 +705,7 @@
        String orgid = loginUser.getUser().getOrgid();
        String configKey = serviceSubtaskCountReq.getConfigKey();
        if (StringUtils.isNotEmpty(configKey)) {
            String configValue = configService.selectConfigByKey(configKey, orgid);
            String configValue = taskSysConfigPort.selectConfigValue(configKey, orgid);
            serviceSubtaskCountReq.setConfigValue(configValue);
            if (StringUtils.isEmpty(configValue)) {
                String logInfo = "getSfStatisticsCount-参数 " + configKey + " æœªé…ç½®, è¯·é…ç½®å¥½åŽé‡è¯•";
@@ -748,7 +714,7 @@
            }
        }
        String followUpCountStyle = configService.selectConfigByKey("followUpCountStyle", orgid);
        String followUpCountStyle = taskSysConfigPort.selectConfigValue("followUpCountStyle", orgid);
        if (ObjectUtils.isNotEmpty(followUpCountStyle)) {
            serviceSubtaskCountReq.setFollowUpCountStyle(followUpCountStyle);
        } else {
@@ -788,7 +754,7 @@
        }
        String configKey = serviceSubtaskCountReq.getConfigKey();
        if (StringUtils.isNotEmpty(configKey)) {
            String configValue = configService.selectConfigByKey(configKey, serviceSubtaskCountReq.getOrgid());
            String configValue = taskSysConfigPort.selectConfigValue(configKey, serviceSubtaskCountReq.getOrgid());
            serviceSubtaskCountReq.setConfigValue(configValue);
            if (StringUtils.isEmpty(configValue)) {
                String logInfo = "getSfStatisticsCount-参数 " + configKey + " æœªé…ç½®, è¯·é…ç½®å¥½åŽé‡è¯•";
@@ -814,7 +780,7 @@
        //复诊统计
        String configKey = serviceSubtaskCountReq.getConfigKey();
        if (StringUtils.isNotEmpty(configKey)) {
            String configValue = configService.selectConfigByKey(configKey, serviceSubtaskCountReq.getOrgid());
            String configValue = taskSysConfigPort.selectConfigValue(configKey, serviceSubtaskCountReq.getOrgid());
            if (StringUtils.isEmpty(configValue)) {
                String logInfo = "getSfStatisticsCountDetails-参数 " + configKey + " æœªé…ç½®, è¯·é…ç½®å¥½åŽé‡è¯•";
                log.error(logInfo);
@@ -870,13 +836,13 @@
        sysUserDept.setOrgid(sysUser.getOrgid());
        sysUserDept.setCampusid(sysUser.getCampusid());
        sysUserDept.setDeptType("1");
        List<SysUserDept> sysUserDepts = sysUserDeptService.selectSysUserDeptList(sysUserDept);
        List<SysUserDept> sysUserDepts = taskSysUserDeptPort.selectSysUserDeptList(sysUserDept);
        SysUserDept sysUserWard = new SysUserDept();
        sysUserWard.setUserId(sysUser.getUserId());
        sysUserWard.setOrgid(sysUser.getOrgid());
        sysUserWard.setCampusid(sysUser.getCampusid());
        sysUserWard.setDeptType("2");
        List<SysUserDept> sysUserWards = sysUserDeptService.selectSysUserDeptList(sysUserWard);
        List<SysUserDept> sysUserWards = taskSysUserDeptPort.selectSysUserDeptList(sysUserWard);
        List<String> deptCodes = sysUserDepts.stream().map(SysUserDept::getDeptCode).collect(Collectors.toList());
        List<String> wardCodes = sysUserWards.stream().map(SysUserDept::getDeptCode).collect(Collectors.toList());
        ServiceSubtaskEntity entity = new ServiceSubtaskEntity();
@@ -953,7 +919,7 @@
    public AjaxResult syncMedInhospForShiyi(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime) {
        // ç«‹å³è¿”回,后台异步执行
        CompletableFuture.runAsync(() -> {
            collectHISService.syncMedInhosp(startTime, endTime);
            taskOpsTriggerApi.syncMedInhosp(startTime, endTime);
        });
        return AjaxResult.success("同步任务已启动,请稍后查看结果");
    }
@@ -961,14 +927,14 @@
    @PostMapping("/syncMedInhospForShiyiYesterday")
    public AjaxResult syncMedInhospForShiyiYesterday() {
        // ç«‹å³è¿”回,后台异步执行
        ryTask.collectHISYesterday();
        taskOpsTriggerApi.collectHISYesterday();
        return AjaxResult.success("同步任务已启动,请稍后查看结果");
    }
    @PostMapping("/syncMedInhospForShiyiDaysBefore")
    public AjaxResult syncMedInhospForShiyiDaysBefore(@RequestParam("day") Long day) {
        // ç«‹å³è¿”回,后台异步执行
        ryTask.collectHISDaysBefore(day);
        taskOpsTriggerApi.collectHISDaysBefore(day);
        return AjaxResult.success("【collectHISDaysBefore】同步任务已启动,天数为" + day + "天,请稍后查看结果");
    }
@@ -976,7 +942,7 @@
    public AjaxResult syncMedOperForShiyi(@RequestParam("startTime") String startTime, @RequestParam("endTime") String endTime) {
        // ç«‹å³è¿”回,后台异步执行
        CompletableFuture.runAsync(() -> {
            collectHISService.syncOper(startTime, endTime);
            taskOpsTriggerApi.syncOper(startTime, endTime);
        });
        return AjaxResult.success("同步任务已启动,请稍后查看结果");
    }
@@ -984,13 +950,13 @@
    @PostMapping("/dealSubtaskDeadline")
    public AjaxResult dealSubtaskDeadline(@RequestParam("orgid") String orgid, @RequestParam("day") Integer day) {
        // ç«‹å³è¿”回,后台异步执行
        ryTask.dealSubtaskDeadline(orgid, day);
        taskOpsTriggerApi.dealSubtaskDeadline(orgid, day);
        return AjaxResult.success("【dealSubtaskDeadline】同步任务已启动,天数为" + day + "天,请稍后查看结果");
    }
    @PostMapping("/sendExceptInfo")
    public AjaxResult sendExceptInfo() throws Exception {
        ryTask.sendExceptInfo();
        taskOpsTriggerApi.sendExceptInfo();
        return AjaxResult.success("sendExceptInfo");
    }
ruoyi-task/src/main/java/com/ruoyi/task/controller/ServiceTaskController.java
ÎļþÃû´Ó ruoyi-admin/src/main/java/com/ruoyi/web/controller/smartor/ServiceTaskController.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.ruoyi.web.controller.smartor;
package com.ruoyi.task.controller;
import com.github.pagehelper.ISelect;
import com.ruoyi.common.annotation.AddOrgId;
@@ -13,10 +13,6 @@
import com.ruoyi.common.utils.RSAPublicKeyExample;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.service.IServiceSubtaskService;
import com.smartor.service.IServiceTaskService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@@ -32,6 +28,12 @@
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.ruoyi.task.service.IServiceTaskService;
import com.ruoyi.task.domain.ServiceTaskVO;
import com.ruoyi.task.domain.ServiceTaskScriptQues;
/**
 * éšè®¿ä»»åŠ¡Controller
ruoyi-task/src/main/java/com/ruoyi/task/domain/DTO/DetailTraceDealDTO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/DTO/DetailTraceDealDTO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain.DTO;
package com.ruoyi.task.domain.DTO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/DTO/ServiceSubtaskDetailDTO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/DTO/ServiceSubtaskDetailDTO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain.DTO;
package com.ruoyi.task.domain.DTO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/IvrTaskVisitResult.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/IvrTaskVisitResult.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import java.util.Date;
ruoyi-task/src/main/java/com/ruoyi/task/domain/PatArchiveSrmVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PatArchiveSrmVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/PatTaskRelevance.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/PatTaskRelevance.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
ruoyi-task/src/main/java/com/ruoyi/task/domain/Question.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/Question.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import lombok.Data;
ruoyi-task/src/main/java/com/ruoyi/task/domain/SendMagParam.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/SendMagParam.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import lombok.Data;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceOutPath.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceOutPath.java ÐÞ¸Ä
@@ -1,10 +1,11 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * ä¸»é”®å¯¹è±¡ service_out_path
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceStatisticsRequest.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceStatisticsRequest.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceStatisticsResponse.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceStatisticsResponse.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubTaskDetailReq.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubTaskDetailReq.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
@@ -6,6 +6,7 @@
import lombok.Data;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
/**
 * ç”µè¯é—®é¢˜å›žç­”明细
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtask.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtask.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
@@ -8,6 +8,7 @@
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * å•一任务(随访)对象 ivr_tasksingle
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCount.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskCount.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskCountReq.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskCountReq.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetail.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetail.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import java.util.Date;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetailTarget.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailTarget.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetailTrace.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailTrace.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import java.util.Date;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDetailVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskDetailVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskDiagname.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskDiagname.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskExport.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskExport.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskIsAbnormalStatistic.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskIsAbnormalStatistic.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskPreachform.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskPreachform.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRecord.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskRecord.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import java.util.Date;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskRes.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskRes.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
@@ -10,6 +10,8 @@
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
/**
 * å•一任务(随访)对象 ivr_tasksingle
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskScriptCount.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskScriptCount.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskStatistic.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskStatistic.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceSubtaskTemplateVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceSubtaskTemplateVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTask.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTask.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskRule.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskRule.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskSchedule.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskSchedule.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskScheduleDetail.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleDetail.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskScheduleVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskScheduleVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
@@ -6,6 +6,8 @@
import lombok.EqualsAndHashCode;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
/**
 * éšè®¿ä»»åŠ¡è®¡åˆ’ VO(主计划 + æ˜Žç»†åˆ—表)
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskScriptQues.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskScriptQues.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
@@ -13,6 +13,8 @@
import com.ruoyi.template.domain.IvrTaskTemplateVO;
import com.ruoyi.template.domain.SvyTaskTemplateVO;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.domain.ServiceTaskScheduleVO;
/**
 * è¯­éŸ³ä»»åŠ¡å¯¹è±¡ ivr_task
 *
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskdept.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskdept.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskdiag.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskdiag.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskmedical.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskmedical.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/ServiceTaskoper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/ServiceTaskoper.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
ruoyi-task/src/main/java/com/ruoyi/task/domain/TaskSendTimeVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/TaskSendTimeVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain;
package com.ruoyi.task.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/DetailTraceDealVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/VO/DetailTraceDealVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain.VO;
package com.ruoyi.task.domain.VO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskCotinueCountVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskCotinueCountVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain.VO;
package com.ruoyi.task.domain.VO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskDetailTraceVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskDetailTraceVO.java ÐÞ¸Ä
@@ -1,15 +1,15 @@
package com.smartor.domain.VO;
package com.ruoyi.task.domain.VO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.smartor.domain.ServiceSubtaskDetailTrace;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
/**
 * éšè®¿ä»»åŠ¡å¼‚å¸¸è®°å½•è·Ÿè¸ªå¯¹è±¡ service_subtask_detail_trace
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceSubtaskVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/VO/ServiceSubtaskVO.java ÐÞ¸Ä
@@ -1,10 +1,9 @@
package com.smartor.domain.VO;
package com.ruoyi.task.domain.VO;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.smartor.domain.TaskSendTimeVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -13,6 +12,7 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.TaskSendTimeVO;
/**
 * å•一任务(随访)对象 ivr_taskcall
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/ServiceTaskmedicalVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/VO/ServiceTaskmedicalVO.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.domain.VO;
package com.ruoyi.task.domain.VO;
import com.ruoyi.common.annotation.Excel;
import io.swagger.annotations.ApiModel;
ruoyi-task/src/main/java/com/ruoyi/task/domain/VO/TransferDeptVO.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/VO/TransferDeptVO.java ÐÞ¸Ä
@@ -1,9 +1,8 @@
package com.smartor.domain.VO;
package com.ruoyi.task.domain.VO;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.smartor.domain.TaskSendTimeVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
ruoyi-task/src/main/java/com/ruoyi/task/domain/entity/ServiceSubtaskEntity.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/domain/entity/ServiceSubtaskEntity.java ÐÞ¸Ä
@@ -1,9 +1,8 @@
package com.smartor.domain.entity;
package com.ruoyi.task.domain.entity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
import com.smartor.domain.TaskSendTimeVO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@@ -12,6 +11,7 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.TaskSendTimeVO;
/**
 * å•一任务(随访)对象 ivr_taskcall
ruoyi-task/src/main/java/com/ruoyi/task/mapper/IvrTaskVisitResultMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/IvrTaskVisitResultMapper.java ÐÞ¸Ä
@@ -1,9 +1,9 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.IvrTaskVisitResult;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import com.ruoyi.task.domain.IvrTaskVisitResult;
/**
 * ä»»åŠ¡éšè®¿ç»“æžœMapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceOutPathMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceOutPathMapper.java ÐÞ¸Ä
@@ -1,9 +1,9 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.smartor.domain.ServiceOutPath;
import com.ruoyi.task.domain.ServiceOutPath;
/**
 * ä¸»é”®Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskDetailMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceSubtaskDetailMapper.java ÐÞ¸Ä
@@ -1,14 +1,14 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.ServiceSubtaskDetail;
import com.smartor.domain.ServiceSubtaskDetailTarget;
import com.smartor.domain.ServiceSubtaskDetailVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.DTO.ServiceSubtaskDetailDTO;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailTarget;
import com.ruoyi.task.domain.ServiceSubtaskDetailVO;
/**
 * è¯­éŸ³ä»»åŠ¡å‘¼å«æ˜Žç»†Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskDetailTraceMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceSubtaskDetailTraceMapper.java ÐÞ¸Ä
@@ -1,13 +1,13 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import java.util.List;
import java.util.Map;
import com.smartor.domain.VO.DetailTraceDealVO;
import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.smartor.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.DetailTraceDealVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
/**
 * éšè®¿ä»»åŠ¡å¼‚å¸¸è®°å½•è·Ÿè¸ªMapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceSubtaskMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceSubtaskMapper.java ÐÞ¸Ä
@@ -1,9 +1,6 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.*;
import com.ruoyi.template.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -14,6 +11,15 @@
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.core.service.dto.PatMedRes;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceStatisticsRequest;
import com.ruoyi.task.domain.ServiceStatisticsResponse;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskCount;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
import com.ruoyi.task.domain.ServiceSubtaskScriptCount;
import com.ruoyi.task.domain.ServiceSubtaskStatistic;
import com.ruoyi.task.domain.VO.ServiceSubtaskCotinueCountVO;
/**
 * å•一任务(随访)Mapper接口
 *
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskMapper.java ÐÞ¸Ä
@@ -1,9 +1,9 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import java.util.List;
import com.smartor.domain.ServiceTask;
import org.apache.ibatis.annotations.Mapper;
import com.ruoyi.task.domain.ServiceTask;
/**
 * è¯­éŸ³ä»»åŠ¡Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskRuleMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskRuleMapper.java ÐÞ¸Ä
@@ -1,8 +1,8 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.smartor.domain.ServiceTaskRule;
import com.ruoyi.task.domain.ServiceTaskRule;
/**
 * ä»»åŠ¡æ‰§è¡Œè§„åˆ™Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskScheduleDetailMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleDetailMapper.java ÐÞ¸Ä
@@ -1,10 +1,10 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.ServiceTaskScheduleDetail;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
/**
 * éšè®¿ä»»åŠ¡è®¡åˆ’æ˜Žç»† Mapper æŽ¥å£
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskScheduleMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskScheduleMapper.java ÐÞ¸Ä
@@ -1,10 +1,10 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.ServiceTaskSchedule;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskSchedule;
/**
 * éšè®¿ä»»åŠ¡è®¡åˆ’ä¸»è¡¨ Mapper æŽ¥å£
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskdeptMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskdeptMapper.java ÐÞ¸Ä
@@ -1,9 +1,9 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.ServiceTaskdept;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskdept;
/**
 * æ¨¡æ¿ç§‘室关联Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskdiagMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskdiagMapper.java ÐÞ¸Ä
@@ -1,8 +1,8 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.smartor.domain.ServiceTaskdiag;
import com.ruoyi.task.domain.ServiceTaskdiag;
/**
 * æ¨¡æ¿ç§‘室关联Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskmedicalMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskmedicalMapper.java ÐÞ¸Ä
@@ -1,10 +1,10 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import com.smartor.domain.ServiceTaskmedical;
import com.smartor.domain.VO.ServiceTaskmedicalVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskmedical;
import com.ruoyi.task.domain.VO.ServiceTaskmedicalVO;
/**
 * ä»»åŠ¡è¯å“å…³è”Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/mapper/ServiceTaskoperMapper.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/mapper/ServiceTaskoperMapper.java ÐÞ¸Ä
@@ -1,8 +1,8 @@
package com.smartor.mapper;
package com.ruoyi.task.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.smartor.domain.ServiceTaskoper;
import com.ruoyi.task.domain.ServiceTaskoper;
/**
 * æ¨¡æ¿ç§‘室关联Mapper接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceOutPathService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceOutPathService.java ÐÞ¸Ä
@@ -1,7 +1,7 @@
package com.smartor.service;
package com.ruoyi.task.service;
import java.util.List;
import com.smartor.domain.ServiceOutPath;
import com.ruoyi.task.domain.ServiceOutPath;
/**
 * ä¸»é”®Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskDetailService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceSubtaskDetailService.java ÐÞ¸Ä
@@ -1,11 +1,11 @@
package com.smartor.service;
package com.ruoyi.task.service;
import com.smartor.domain.ServiceSubtaskDetail;
import com.smartor.domain.ServiceSubtaskDetailTarget;
import com.smartor.domain.ServiceSubtaskDetailVO;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailTarget;
import com.ruoyi.task.domain.ServiceSubtaskDetailVO;
/**
 * è¯­éŸ³ä»»åŠ¡å‘¼å«æ˜Žç»†Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskDetailTraceService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceSubtaskDetailTraceService.java ÐÞ¸Ä
@@ -1,13 +1,12 @@
package com.smartor.service;
package com.ruoyi.task.service;
import java.util.List;
import java.util.Map;
import com.smartor.domain.DTO.DetailTraceDealDTO;
import com.smartor.domain.ServiceSubtaskDetailTrace;
import com.smartor.domain.VO.DetailTraceDealVO;
import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO;
import org.springframework.web.bind.annotation.RequestBody;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.DetailTraceDealVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
/**
 * éšè®¿ä»»åŠ¡å¼‚å¸¸è®°å½•è·Ÿè¸ªService接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceSubtaskService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceSubtaskService.java ÐÞ¸Ä
@@ -1,10 +1,6 @@
package com.smartor.service;
package com.ruoyi.task.service;
import com.ruoyi.common.core.domain.AjaxResult;
import com.smartor.domain.*;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.VO.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Date;
@@ -13,6 +9,23 @@
import com.ruoyi.template.domain.VO.HeLibraryCountVO;
import com.ruoyi.template.domain.IvrTaskTemplateTargetoption;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceStatisticsRequest;
import com.ruoyi.task.domain.ServiceStatisticsResponse;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskCount;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
import com.ruoyi.task.domain.ServiceSubtaskScriptCount;
import com.ruoyi.task.domain.ServiceSubtaskStatistic;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.VO.ServiceSubtaskCotinueCountVO;
import com.ruoyi.task.domain.PatArchiveSrmVO;
import com.ruoyi.task.domain.ServiceSubtaskDiagname;
import com.ruoyi.task.domain.ServiceSubtaskIsAbnormalStatistic;
import com.ruoyi.task.domain.ServiceSubtaskRes;
import com.ruoyi.task.domain.ServiceSubtaskTemplateVO;
import com.ruoyi.task.domain.ServiceTaskVO;
import com.ruoyi.task.domain.VO.TransferDeptVO;
/**
 * å•一任务(随访)Service接口
 *
@@ -118,40 +131,17 @@
    public Map<String, Integer> insertOrUpdateTask(ServiceTaskVO ivrTaskVO);
    /**
     * ç”µè¯å›žè°ƒä»»åŠ¡
     *
     * @param phoneCallBackVO
     * @return ç»“æžœ
    /*
     * ------------------------------------------------------------------
     * ç”µè¯ç›¸å…³çš„ 7 ä¸ªæ–¹æ³•(phoneCallBack / phoneCallBackYQ / getFirstQues /
     * processResust / taskPull / saveQuestionAnswerPhone / recordAccept)
     * å·²éšç”µè¯æ¨¡å—化整体搬进 ruoyi-phone:
     *   æŽ¥å£ -> com.ruoyi.phone.service.IPhoneService
     *   å®žçް -> com.ruoyi.phone.service.impl.PhoneServiceImpl
     *   å¯¹å¤– -> com.ruoyi.phone.api.PhoneFacade(其它模块从这里调电话能力)
     * URL ä¿æŒä¸å˜ï¼Œçº¿ä¸Šç”µè¯å¹³å°æ— éœ€æ”¹åŠ¨ã€‚
     * ------------------------------------------------------------------
     */
    public void phoneCallBack(PhoneCallBackVO phoneCallBackVO);
    /**
     * ç”µè¯ASR通话回调(雨绮)
     *
     * @param phoneCallReqYQVO
     * @return ç»“æžœ
     */
    public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO);
    public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO);
    public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallReqYQVO);
    /**
     * é›¨ç»®ä»»åŠ¡æ‹‰å–
     *
     * @return
     */
    public List<PullTaskVO> taskPull();
    public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq);
    public Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO);
    /**
     * èŽ·å–éšè®¿ã€å¤è¯Šé‡
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskScheduleService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceTaskScheduleService.java ÐÞ¸Ä
@@ -1,10 +1,10 @@
package com.smartor.service;
package com.ruoyi.task.service;
import com.smartor.domain.ServiceTaskSchedule;
import com.smartor.domain.ServiceTaskScheduleDetail;
import com.smartor.domain.ServiceTaskScheduleVO;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
import com.ruoyi.task.domain.ServiceTaskScheduleVO;
/**
 * éšè®¿ä»»åŠ¡è®¡åˆ’ Service æŽ¥å£
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceTaskService.java ÐÞ¸Ä
@@ -1,12 +1,12 @@
package com.smartor.service;
package com.ruoyi.task.service;
import java.util.List;
import java.util.Map;
import com.ruoyi.template.domain.IvrLibaTemplateScriptVO;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.ServiceTask;
import com.ruoyi.template.domain.SvyLibTemplateScriptVO;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
/**
 * è¯­éŸ³ä»»åŠ¡Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdeptService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceTaskdeptService.java ÐÞ¸Ä
@@ -1,8 +1,8 @@
package com.smartor.service;
package com.ruoyi.task.service;
import com.smartor.domain.ServiceTaskdept;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskdept;
/**
 * æ¨¡æ¿ç§‘室关联Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskdiagService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceTaskdiagService.java ÐÞ¸Ä
@@ -1,7 +1,7 @@
package com.smartor.service;
package com.ruoyi.task.service;
import java.util.List;
import com.smartor.domain.ServiceTaskdiag;
import com.ruoyi.task.domain.ServiceTaskdiag;
/**
 * æ¨¡æ¿ç§‘室关联Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskmedicalService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceTaskmedicalService.java ÐÞ¸Ä
@@ -1,9 +1,9 @@
package com.smartor.service;
package com.ruoyi.task.service;
import com.smartor.domain.ServiceTaskmedical;
import com.smartor.domain.VO.ServiceTaskmedicalVO;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskmedical;
import com.ruoyi.task.domain.VO.ServiceTaskmedicalVO;
/**
 * ä»»åŠ¡è¯å“å…³è”Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/IServiceTaskoperService.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/IServiceTaskoperService.java ÐÞ¸Ä
@@ -1,7 +1,7 @@
package com.smartor.service;
package com.ruoyi.task.service;
import java.util.List;
import com.smartor.domain.ServiceTaskoper;
import com.ruoyi.task.domain.ServiceTaskoper;
/**
 * æ¨¡æ¿ç§‘室关联Service接口
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceOutPathServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceOutPathServiceImpl.java ÐÞ¸Ä
@@ -1,19 +1,19 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.RSAPublicKeyExample;
import com.ruoyi.common.utils.StringUtils;
import com.smartor.domain.ServiceOutPath;
import com.smartor.domain.ServiceTask;
import com.smartor.mapper.ServiceOutPathMapper;
import com.smartor.mapper.ServiceTaskMapper;
import com.smartor.service.IServiceOutPathService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.List;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.mapper.ServiceTaskMapper;
import com.ruoyi.task.domain.ServiceOutPath;
import com.ruoyi.task.mapper.ServiceOutPathMapper;
import com.ruoyi.task.service.IServiceOutPathService;
/**
 * ä¸»é”®Service业务层处理
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskDetailServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskDetailServiceImpl.java ÐÞ¸Ä
@@ -1,15 +1,9 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.smartor.domain.*;
import com.smartor.domain.ServiceSubtaskDetail;
import com.smartor.mapper.ServiceSubtaskDetailMapper;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.smartor.service.IServiceSubtaskDetailService;
import com.smartor.service.IServiceTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -19,6 +13,15 @@
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailTarget;
import com.ruoyi.task.domain.ServiceSubtaskDetailVO;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.mapper.ServiceSubtaskDetailMapper;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.service.IServiceSubtaskDetailService;
import com.ruoyi.task.service.IServiceTaskService;
/**
 * è¯­éŸ³ä»»åŠ¡å‘¼å«æ˜Žç»†Service业务层处理
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskDetailTraceServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskDetailTraceServiceImpl.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import java.util.*;
import java.util.stream.Collectors;
@@ -7,16 +7,16 @@
import com.github.pagehelper.util.StringUtil;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.smartor.domain.*;
import com.smartor.domain.DTO.DetailTraceDealDTO;
import com.smartor.domain.VO.DetailTraceDealVO;
import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.mapper.ServiceSubtaskDetailMapper;
import com.smartor.mapper.ServiceSubtaskDetailTraceMapper;
import com.smartor.service.*;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.task.mapper.ServiceSubtaskDetailMapper;
import com.ruoyi.task.domain.DTO.DetailTraceDealDTO;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.DetailTraceDealVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.ruoyi.task.mapper.ServiceSubtaskDetailTraceMapper;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
/**
 * éšè®¿ä»»åŠ¡å¼‚å¸¸è®°å½•è·Ÿè¸ªService业务层处理
@@ -32,12 +32,9 @@
    @Autowired
    private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper;
    @Autowired
    private ISvyLibTemplateScriptService isvyLibTemplateScriptService;
    // è¯´æ˜Žï¼šåŽŸå®žçŽ°é‡Œè¿˜æ³¨å…¥äº†ä¸€ä¸ª smartor çš„ ISvyLibTemplateScriptService,
    // ä½†å…¨ç±»ä»Žæœªä½¿ç”¨ï¼ˆæ­»æ³¨å…¥ï¼‰ã€‚随本类搬入 ruoyi-task æ—¶åˆ é™¤ â€”—
    // ä¿ç•™å®ƒä¼šè®© ruoyi-task åå‘依赖 smartor,形成硬循环。
    /**
     * æŸ¥è¯¢éšè®¿ä»»åŠ¡å¼‚å¸¸è®°å½•è·Ÿè¸ª
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceSubtaskServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskServiceImpl.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
@@ -20,28 +20,13 @@
import com.ruoyi.message.sms.SmsSenderFactory;
import com.ruoyi.message.wechat.WechatSendRequest;
import com.ruoyi.message.wechat.WechatSenderFactory;
import com.ruoyi.system.domain.SysConfig;
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.common.FtpService;
import com.smartor.common.ScheduleSubtaskBuilder;
import com.ruoyi.common.utils.PhoneUtils;
import com.smartor.domain.DTO.*;
import com.smartor.domain.*;
import com.smartor.domain.VO.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
@@ -57,8 +42,6 @@
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -75,9 +58,7 @@
import com.ruoyi.template.domain.IvrTaskTemplateScriptVO;
import com.ruoyi.template.domain.IvrTaskTemplateTargetoption;
import com.ruoyi.template.domain.IvrTaskTemplateVO;
import com.smartor.domain.IvrTaskVisitResult;
import com.ruoyi.template.mapper.IvrTaskTemplateTargetoptionMapper;
import com.smartor.mapper.IvrTaskVisitResultMapper;
import com.ruoyi.template.service.IIvrTaskTemplateScriptService;
import com.ruoyi.template.service.IIvrTaskTemplateService;
import com.ruoyi.template.service.IIvrTaskTemplateTargetoptionService;
@@ -92,6 +73,57 @@
import com.ruoyi.template.service.ISvyTaskTemplateScriptService;
import com.ruoyi.template.service.ISvyTaskTemplateService;
import static cn.hutool.core.convert.Convert.toHex;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.DTO.ServiceSubtaskDetailDTO;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceStatisticsRequest;
import com.ruoyi.task.domain.ServiceStatisticsResponse;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskCount;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailVO;
import com.ruoyi.task.domain.ServiceSubtaskScriptCount;
import com.ruoyi.task.domain.ServiceSubtaskStatistic;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.ServiceTaskdept;
import com.ruoyi.task.domain.ServiceTaskdiag;
import com.ruoyi.task.domain.ServiceTaskmedical;
import com.ruoyi.task.domain.ServiceTaskoper;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.domain.VO.ServiceSubtaskCotinueCountVO;
import com.ruoyi.task.mapper.ServiceSubtaskDetailMapper;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.ServiceOutPath;
import com.ruoyi.task.mapper.ServiceOutPathMapper;
import com.ruoyi.task.service.IServiceOutPathService;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.api.spi.TaskPreachformPort;
import com.ruoyi.task.api.spi.TaskArchivePort;
import com.ruoyi.task.api.spi.TaskFtpPort;
import com.ruoyi.task.api.spi.TaskSysConfigPort;
import com.ruoyi.task.api.spi.TaskSysDeptPort;
import com.ruoyi.task.common.ScheduleSubtaskBuilder;
import com.ruoyi.task.domain.ServiceTaskScheduleVO;
import com.ruoyi.task.service.IServiceSubtaskService;
import com.ruoyi.task.service.IServiceTaskScheduleService;
import com.ruoyi.task.service.IServiceTaskService;
import com.ruoyi.task.service.IServiceTaskdeptService;
import com.ruoyi.task.service.IServiceTaskdiagService;
import com.ruoyi.task.service.IServiceTaskmedicalService;
import com.ruoyi.task.service.IServiceTaskoperService;
import com.ruoyi.task.domain.PatArchiveSrmVO;
import com.ruoyi.task.domain.PatTaskRelevance;
import com.ruoyi.task.domain.SendMagParam;
import com.ruoyi.task.domain.ServiceSubtaskDiagname;
import com.ruoyi.task.domain.ServiceSubtaskIsAbnormalStatistic;
import com.ruoyi.task.domain.ServiceSubtaskRes;
import com.ruoyi.task.domain.ServiceSubtaskTemplateVO;
import com.ruoyi.task.domain.ServiceTaskVO;
import com.ruoyi.task.domain.VO.TransferDeptVO;
/**
 * å•一任务(随访)Service业务层处理
@@ -134,7 +166,7 @@
    private ServiceSubtaskMapper serviceSubtaskMapper;
    @Autowired
    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
    private TaskPreachformPort taskPreachformPort;
    @Autowired
    private ServiceSubtaskDetailMapper serviceSubtaskDetailMapper;
@@ -144,9 +176,6 @@
    @Autowired
    private IvrTaskTemplateTargetoptionMapper serviceTaskScriptTargetoptionMapper;
    @Autowired
    private IvrTaskVisitResultMapper serviceTaskVisitResultMapper;
    @Autowired
    private IServiceTaskdeptService serviceTaskdeptService;
@@ -184,45 +213,30 @@
    private IServiceOutPathService iServiceOutPathService;
    @Autowired
    private IServiceSubtaskDetailTraceService traceService;
    @Autowired
    private IServiceTaskScheduleService serviceTaskScheduleService;
    @Autowired
    private IPatArchiveService patArchiveService;
    private TaskArchivePort taskArchivePort;
    @Autowired
    private ScheduleSubtaskBuilder scheduleSubtaskBuilder;
    @Autowired
    private FtpService ftpService;
    private TaskFtpPort taskFtpPort;
    @Autowired
    private PatArchiveMapper patArchiveMapper;
    @Autowired
    private SvyLibScriptCategoryMapper svyLibScriptCategoryMapper;
    @Autowired
    private IPatMedInhospService patMedInhospService;
    @Value("${pri_key}")
    private String pri_key;
    @Value("${pub_key}")
    private String pub_key;
    @Value("${ASRCallBackPath}")
    private String ASRCallBackPath;
    @Autowired
    private ISysConfigService configService;
    @Autowired
    private SysConfigMapper sysConfigMapper;
    private TaskSysConfigPort taskSysConfigPort;
    /**
     * æ¶ˆæ¯å‘送方工厂(短信/微信公众号),替代原来直接调用各站点发送代码的写法
@@ -233,36 +247,15 @@
    @Autowired
    private WechatSenderFactory wechatSenderFactory;
    @Autowired
    private RedisTemplate redisTemplate;
    @Value("${hangup}")
    private String hangup;
    @Value("${app_key_yq}")
    private String app_key_yq;
    @Value("${ruoyi.profile}")
    private String profile;
    @Value("${voicePathPrefix}")
    private String voicePathPrefix;
    @Value("${localIP}")
    private String localIP;
    @Value("${req_path}")
    private String req_path;
    @Value("${phoneUpEveryHour}")
    private Integer phoneUpEveryHour;
    @Value("${phoneEndHour}")
    private Integer phoneEndHour;
    @Autowired
    private IPatMedOuthospService patMedOuthospService;
    @Autowired
    private SysDept2Mapper sysDept2Mapper;
    private TaskSysDeptPort taskSysDeptPort;
    @Value("${spring.profiles.active}")
    private String active;
@@ -374,7 +367,7 @@
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        List<ServiceSubtaskPreachform> serviceSubtaskPreachformLists = serviceSubtaskPreachformMapper.selectBySubtask(serviceSubtask);
        List<ServiceSubtaskPreachform> serviceSubtaskPreachformLists = taskPreachformPort.selectBySubtask(serviceSubtask);
        for (ServiceSubtask serviceSubtask1 : list) {
            PatTaskRelevance patTaskRelevance = new PatTaskRelevance();
            if (!serviceSubtask1.getHospType().equals("2")) {
@@ -495,7 +488,7 @@
            }
            PatMedInhosp inhosp = inhospVisitFacade.selectPatMedInhospByInhospid(serviceSubtask.getInhospid());
            if (ObjectUtils.isNotEmpty(inhosp)) {
                PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(inhosp.getPatid());
                PatArchive patArchive = taskArchivePort.findByPatid(inhosp.getPatid());
                if (ObjectUtils.isNotEmpty(patArchive)) {
                    Map<String, String> stringStringMap = calculateAge(patArchive.getBirthdate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate(), LocalDate.now());
                    serviceSubtaskRes.setAge(StringUtils.isNotEmpty(stringStringMap.get("age")) ? Long.valueOf(stringStringMap.get("age")) : null);
@@ -524,7 +517,7 @@
        //患者获取随访任务-需要查询同个患者(身份证)所有的任务 æ™¯å®
        if (StringUtils.isNotEmpty(serviceSubtaskEntity.getOrgid()) && serviceSubtaskEntity.getOrgid().equals("47255004333112711A1001")) {
            if (ObjectUtils.isNotEmpty(serviceSubtaskEntity.getPatid())) {
                PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtaskEntity.getPatid());
                PatArchive patArchive = taskArchivePort.findByPatid(serviceSubtaskEntity.getPatid());
                if (ObjectUtils.isNotEmpty(patArchive) && StringUtils.isNotEmpty(patArchive.getIdcardno())) {
                    serviceSubtaskEntity.setPatid(null);
                    serviceSubtaskEntity.setSfzh(patArchive.getIdcardno());
@@ -827,7 +820,7 @@
        patMedInhosp.setPatid(serviceSubtask1.getPatid());
        patMedInhosp.setInhospstate("0");
        List<PatMedInhosp> patMedInhospList = inhospVisitFacade.selectPatMedInhospList(patMedInhosp);
        PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(serviceSubtask1.getPatid());
        PatArchive patArchive = taskArchivePort.findByPatid(serviceSubtask1.getPatid());
        //景宁 æ–°å¢žå†æ¬¡éšè®¿ä¸èƒ½è§¦å‘患者再入院
        boolean inhospAgain = true;
        if (serviceSubtask1.getOrgid().equals("47255004333112711A1001")) {
@@ -1001,7 +994,7 @@
            }
            serviceTask.setLeaveldeptcodes(serviceTaskVO.getLeaveldeptcodes());
            if (StringUtils.isEmpty(serviceTask.getLeavehospitaldistrictname()) && StringUtils.isNotEmpty(serviceTask.getLeavehospitaldistrictcode())) {
                SysDept sysDept = sysDept2Mapper.selectDeptByCode(serviceTask.getLeavehospitaldistrictcode(), null);
                SysDept sysDept = taskSysDeptPort.selectDeptByCode(serviceTask.getLeavehospitaldistrictcode(), null);
                serviceTask.setLeavehospitaldistrictname(sysDept.getDeptName());
            }
            if (serviceTaskVO.getLongTask() == 1) serviceTask.setSendState(2L);
@@ -1230,7 +1223,7 @@
                            serviceSubtaskPreachform.setSendstate("1");
                            serviceSubtaskPreachform.setOrgid(serviceTask.getOrgid());
                            serviceSubtaskPreachform.setCreateTime(new Date());
                            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
                            taskPreachformPort.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
                        }
                    }
                }
@@ -1283,7 +1276,7 @@
                            if (CollectionUtils.isNotEmpty(patMedOuthosps)) {
                                PatMedOuthosp patMedOuthosp = patMedOuthosps.get(0);
                                patMedOuthosp.setServerState("0");
                                patMedOuthospService.updatePatMedOuthosp(patMedOuthosp);
                                outhospDataFacade.update(patMedOuthosp);
                            }
                        }
                    } else if (serviceSubtask.getServiceType().equals("19")) {
@@ -1599,7 +1592,7 @@
            List<ServiceSubtask> ssTaskList = serviceSubtaskMapper.selectServiceSubtaskBySendstate(ssVo);
            for (int i = 0; i < ssTaskList.size(); i++) {
                ServiceSubtask serviceSubtask = ssTaskList.get(i);
                serviceSubtaskPreachformMapper.deleteServiceSubtaskPreachformBySubid(serviceSubtask.getId());
                taskPreachformPort.deleteServiceSubtaskPreachformBySubid(serviceSubtask.getId());
                if (StringUtils.isNotEmpty(serviceTaskVO.getPreachformList())) {
                    List<Map<String, Object>> pfList = serviceTaskVO.getPreachformList();
                    for (Map<String, Object> map : pfList) {
@@ -1613,7 +1606,7 @@
                        serviceSubtaskPreachform.setSendstate("1");
                        serviceSubtaskPreachform.setOrgid(serviceTask.getOrgid());
//                        serviceSubtaskPreachform.setSort(Long.valueOf(i));
                        serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
                        taskPreachformPort.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
                    }
                }
            }
@@ -1621,1239 +1614,6 @@
        Map<String, Integer> map = new HashMap<>();
        map.put("taskId", serviceTask.getTaskid().intValue());
        return map;
    }
    @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<String, Object> map = redisCache.getCacheObject(phoneCallBackVO.getUuid());
        ServiceSubtask serviceSubtask = (ServiceSubtask) map.get("ServiceSubtask");
        List<IvrTaskTemplateScriptVO> IvrTaskTemplateScriptVOs = (List<IvrTaskTemplateScriptVO>) 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()) {
                    ServiceSubtask ss = new ServiceSubtask();
                    ss.setId(serviceSubtask.getId());
                    ss.setSendstate(5L);
                    ss.setRemark("电话无响应");
                    serviceSubtaskMapper.updateServiceSubtask(ss);
                    //没有下一题了,就挂断电话,播放结束语
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "hangup", 1, 120, TimeUnit.MINUTES);
                    phoneUtils.ttsPlayback(ivrTaskTemplateVO.getRevisitAfter(), phoneCallBackVO.getUuid());
                    return;
                } else {
                    //有下一题
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "noVoice", 0, 120, TimeUnit.MINUTES);
                    IvrTaskTemplateScriptVO nextQuestion = getNextQuestion(IvrTaskTemplateScriptVOs, nowQuestion);
                    // é—®é¢˜ï¼Œ  åŽ»è°ƒç”¨"tts合成和播放"接口
                    String date = simpleDateFormat1.format(new Date());
                    log.info("去调用tts合成和播放接口: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                    phoneUtils.ttsPlayback(nowQuestion.getIvrtext(), 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.getIvrtext(), 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());
                }
                log.error("PCB--getQuestionText问题为:{},UUID:{}", nowQuestion.getIvrtext(), phoneCallBackVO.getUuid());
                if (StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && matcher2.matches() && StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && matcher.matches() || StringUtils.isEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && matcher2.matches() || StringUtils.isEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex2()) && StringUtils.isNotEmpty(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetregex()) && matcher.matches()) {
                    //说明匹配正确了
                    //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法
                    nowQuestion.getIvrTaskScriptTargetoptionList().get(j).setIsUserOperation(1);
                    serviceTaskScriptTargetoptionMapper.updateIvrTaskTemplateTargetoption(nowQuestion.getIvrTaskScriptTargetoptionList().get(j));
                    //将静默置为0
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "noVoice", 0, 120, TimeUnit.MINUTES);
                    redisCache.setCacheObject(phoneCallBackVO.getUuid() + "mateNum", 0, 120, TimeUnit.MINUTES);
                    //将患者的回签写进表里
                    IvrTaskVisitResult serviceTaskVisitResult = DtoConversionUtils.sourceToTarget(serviceSubtask, IvrTaskVisitResult.class);
                    serviceTaskVisitResult.setId(null);
                    serviceTaskVisitResult.setQuestion(nowQuestion.getIvrtext());
                    serviceTaskVisitResult.setPatientAnswer(phoneCallBackVO.getTextResult());
                    serviceTaskVisitResult.setCreateTime(new Date());
                    serviceTaskVisitResult.setOptionResult(nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getTargetvalue());
                    serviceTaskVisitResultMapper.insertIvrTaskVisitResult(serviceTaskVisitResult);
                    //将匹配到的标识改成true
                    isppd = true;
                    //获取下一题
                    Long nextQuestion = nowQuestion.getIvrTaskScriptTargetoptionList().get(j).getNextQuestion();
                    for (IvrTaskTemplateScriptVO script : IvrTaskTemplateScriptVOs) {
                        if (script.getTargetid() == nextQuestion) {
                            QuestionMessage questionMessage = new QuestionMessage();
                            questionMessage.setNowQuestion(script);
                            questionMessage.setQuestionList(IvrTaskTemplateScriptVOs);
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                            phoneUtils.ttsPlayback(script.getIvrtext(), phoneCallBackVO.getUuid());
                            return;
                        } else if (nextQuestion > IvrTaskTemplateScriptVOs.size()) {
                            //没有下一题了,就结束了
                            ServiceSubtask ss = new ServiceSubtask();
                            ss.setId(serviceSubtask.getId());
                            ss.setSendstate(6L);
                            serviceSubtaskMapper.updateServiceSubtask(ss);
                            String date = simpleDateFormat1.format(new Date());
                            log.error("没有下一题了,就结束了: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "hangup", 1, 120, TimeUnit.MINUTES);
                            phoneUtils.ttsPlayback(ivrTaskTemplateVO.getRevisitAfter(), phoneCallBackVO.getUuid());
                            try {
                                Thread.sleep(3000);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                            phoneUtils.hangup("", "", ivrTaskTemplateVO.getRevisitAfter(), "", "", "", "", phoneCallBackVO.getUuid());
                            return;
                        }
                    }
                    return;
                } else {
                    //没有匹配上当前option
                    //Targetregex2 ä¸ºfalse,表示在Targetregex2中存在  è¯­å¥ä¸­çš„关键字,这个option就不用再继续匹配了,直接匹配下一个option
                    continue;
                }
            }
            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);
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "mateNum", 0, 120, TimeUnit.MINUTES);
                        String date = simpleDateFormat1.format(new Date());
                        log.info("如果下一题为空.则新的数据返回,并加上感谢语: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                        phoneUtils.ttsPlayback(nextQuestion.getIvrtext(), phoneCallBackVO.getUuid());
                        return;
                    } else {
                        //就可以挂断电话了
                        ServiceSubtask ss = new ServiceSubtask();
                        ss.setId(serviceSubtask.getId());
                        ss.setSendstate(6L);
                        serviceSubtaskMapper.updateServiceSubtask(ss);
                        String date = simpleDateFormat1.format(new Date());
                        log.info("就可以挂断电话了------: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "hangup", 1, 120, TimeUnit.MINUTES);
                        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<String> list = Arrays.asList(split);
            List<Long> list1 = new ArrayList<>();
            if (StringUtils.isNotEmpty(extemplateID)) {
                for (String str : list) {
                    list1.add(Long.valueOf(str));
                }
                List<IvrLibaExtemplatescript> 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(returnQues.getContent());
                    }
                    Matcher matcher2 = null;
                    if (StringUtils.isNotEmpty(ivrLibaExtemplatescript.getSelfRegex2())) {
                        Pattern pattern2 = Pattern.compile(ivrLibaExtemplatescript.getSelfRegex2());
                        matcher2 = pattern2.matcher(returnQues.getContent());
                    }
                    log.error("++++++++++++++++++++++++++通用库是否为空:selfRegex : {} , selfRegex2 : {}", ivrLibaExtemplatescript.getSelfRegex(), ivrLibaExtemplatescript.getSelfRegex2());
                    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()) {
                        QuestionMessage questionMessage = redisCache.getCacheObject(phoneCallBackVO.getUuid() + "returnQues");
                        IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = returnQues.getNowQuestion();
                        ivrTaskTemplateScriptVO.setSubmoduleText(ivrLibaExtemplatescript.getSwitchText());
                        ivrTaskTemplateScriptVO.setSubmoduleVoice(ivrLibaExtemplatescript.getSwitchWav());
                        redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                        if (ivrLibaExtemplatescript.getIsEnd() == 1) {
                            //将问题置空
                            IvrTaskTemplateScriptVO nowQuestion1 = questionMessage.getNowQuestion();
                            nowQuestion1.setScriptContent(null);
                            nowQuestion1.setScriptVoice(null);
                            questionMessage.setNowQuestion(nowQuestion1);
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "returnQues", questionMessage, 120, TimeUnit.MINUTES);
                            redisCache.setCacheObject(phoneCallBackVO.getUuid() + "isOver", 1, 120, TimeUnit.MINUTES);
                        }
                        //调用"15、tts合成和播放, tts_playback"将结果传回
                        String date = simpleDateFormat1.format(new Date());
                        log.info("调用tts合成和播放------: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                        phoneUtils.ttsPlayback(nowQuestion.getIvrtext() + ivrTaskTemplateScriptVO.getSubmoduleText(), phoneCallBackVO.getUuid());
                    }
                    break;
                }
                String date = simpleDateFormat1.format(new Date());
                log.info("最后的信息回复-: {},uuid为:{}", date, phoneCallBackVO.getUuid());
                phoneUtils.ttsPlayback(nowQuestion.getIvrtext(), phoneCallBackVO.getUuid());
            }
        }
    }
    /**
     * ç”µè¯ASR通话回调(雨绮)
     *
     * @param phoneCallReqYQVO
     */
    @Override
    public PhoneCallBackYQVO phoneCallBackYQ(PhoneCallReqYQVO phoneCallReqYQVO) {
        //先把该subId从cache_exist中剔除,只要有回调就剔除(目的是,尽快让cache-exist为空,去缓存下一批要打的电话;这里剔除不影响电话的正常业务)
//        redisCache.removeElementFromList("cache-exist", phoneCallReqYQVO.getTaskid());
        //判断是否挂机
        String hangupState = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "- hangup");
        Object cacheObject1 = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
        if (StringUtils.isNotEmpty(hangupState) && hangupState.equals("1") || ObjectUtils.isNotEmpty(cacheObject1) && String.valueOf(cacheObject1).equals("1")) {
            return new PhoneCallBackYQVO();
        }
        if ("AsrCallback".equals(phoneCallReqYQVO.getOperate()) && phoneCallReqYQVO.getEnd_time() != null && phoneCallReqYQVO.getEnd_time() != -1) {
            //处理挂断
            if (phoneCallReqYQVO.getAsrtext().contains("拨叫的用户正忙")) {
                //判断 recordAccept-hungup æ˜¯å¦æœ‰å€¼ï¼Œå¦‚果有,则说明不用处理了,之前已经处理过了(recordAccept-hungup这个主要是给"通话记录用的,")
                Object cacheObject = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
                if (ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
                    ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                    setFailPreachForm(serviceSubtask, "3", "拨叫的用户正忙", "4");
                    return new PhoneCallBackYQVO();
                }
                //如果发送状态为6,就说明已经打完了,也没有必要继续往下走了
                if (StringUtils.isNotEmpty(phoneCallReqYQVO.getTaskid())) {
                    ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                    if (serviceSubtask.getSendstate() == 6L) return new PhoneCallBackYQVO();
                }
                ServiceSubtask serviceSubtask2 = new ServiceSubtask();
                serviceSubtask2.setRemark("电话发送成功(患者挂断)");
                serviceSubtask2.setSenduuid(phoneCallReqYQVO.getUuid());
                serviceSubtask2.setId(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask2);
                Map<String, String> req = new HashMap<>();
                req.put("uuid", phoneCallReqYQVO.getUuid());
                req.put("caller", phoneCallReqYQVO.getPhone());
                HttpUtil.postJsonRequest(hangup, new Gson().toJson(req));
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- hangup", "1", 2, TimeUnit.MINUTES);
                //先把默认的recordAccept-hungup删除,再更新
                redisCache.deleteObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
                redisCache.setCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup", "1", 10, TimeUnit.MINUTES);
                //记录电话发送状态
                setFailPreachForm(serviceSubtask2, "3", "电话发送成功(患者挂断)", "4");
                //这个时候就需要走补偿机制了,先暂定走短信
//                getSmsCompensate(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                return new PhoneCallBackYQVO();
            }
        }
        //定义一个分数的变量
        Boolean aBoolean1 = redisCache.hasKey(phoneCallReqYQVO.getUuid() + "SCORE");
        if (!aBoolean1) redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", 0.0, 120, TimeUnit.MINUTES);
        PhoneCallBackYQVO phoneCallBackYQVO = new PhoneCallBackYQVO();
        //channel_create é€šé“创建的时候,可以执行一些其它操作,譬如发个短信之类的;  æˆ‘们的业务可以不用管    PlayEventCallback è¿™ä¸ªæ˜¯æ’­æ”¾è¯­éŸ³çš„,暂时用不到     End_time()= -1或null表示当前的asrtext不是一句完整的话
        if (phoneCallReqYQVO.getOperate().equals("channel_create")) {
            return phoneCallBackYQVO;
        }
        //PlayEventCallback è¿™ä¸ªæ˜¯æ’­æ”¾è¯­éŸ³çš„    playstart:放音开始(问题播报开始)    playstop: æ”¾éŸ³ç»“束(问题播报结束)
        if (phoneCallReqYQVO.getOperate().equals("PlayEventCallback")) {
            String cacheJSY = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "- jsy");
            if (phoneCallReqYQVO.getOperate().equals("PlayEventCallback") && phoneCallReqYQVO.getPlaystop() == false) {
                //判断redis中是否有结束语
                if (StringUtils.isEmpty(cacheJSY)) {
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop", false, 120, TimeUnit.MINUTES);
                    log.error("PlayEventCallbackPlaystop的值为-------:{}", false);
                }
            } else {
                //如果结束语不为空,则要挂电话了
                if (StringUtils.isNotEmpty(cacheJSY)) {
                    Map<String, String> req = new HashMap<>();
                    req.put("uuid", phoneCallReqYQVO.getUuid());
                    req.put("caller", phoneCallReqYQVO.getPhone());
//                    HttpUtils.sendPost(hangup, new Gson().toJson(req));
                    HttpUtil.postJsonRequest(hangup, new Gson().toJson(req));
                    ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
                    //记录电话发送状态
                    setFailPreachForm(serviceSubtask, "3", "通话正常结束", "9");
                    //删除结束语的缓存
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "- jsy");
                } else {
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop", true, 120, TimeUnit.MINUTES);
                    log.error("-------PlayEventCallbackPlaystop的值为:{}", true);
                }
            }
            return phoneCallBackYQVO;
        }
        //获取放音是否结束
        Object isPlayEventOverObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
        if (ObjectUtils.isNotEmpty(isPlayEventOverObj)) {
            Boolean isPlayEventOver = (Boolean) isPlayEventOverObj;
            if (!isPlayEventOver) {
                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<IvrTaskTemplateScript> 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);
            //如果不存在,就把当前的UUID做为key,放到对象中去
            redisCache.setCacheObject(phoneCallReqYQVO.getUuid(), phoneCallReqYQVO, 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.getIvrtext();
                log.error("SilentCallback的问题内容scriptContent:{}", scriptContent);
                phoneCallBackYQVO.setValue(getObject(serviceSubtask, scriptContent));
                //将静默次数加1
                Integer noVoiceNum = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "noVoice");
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "noVoice", noVoiceNum + 1, 120, TimeUnit.MINUTES);
                return phoneCallBackYQVO;
            } else {
                log.error("静默次数达到,挂掉电话:{}", num);
                ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                ServiceSubtask ss = new ServiceSubtask();
                ss.setId(serviceSubtask.getId());
                ss.setSendstate(5L);
                ss.setRemark("电话静默");
                serviceSubtaskMapper.updateServiceSubtask(ss);
                //大与等于的话,直接挂断
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                phoneCallBackYQVO.setValue(serviceTask1.getJsy());
                //将结果写到detail中
                ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate));
                serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                //去redis中,把该子任务ID删除
                Long id = serviceSubtask.getId();
                //先更新一下分数
                double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                serviceSubtask.setScore(String.valueOf(score));
                serviceSubtask.setFinishtime(new Date());
                serviceSubtask.setUpdateTime(new Date());
                log.error("先更新一下分数,电话的serviceSubtask的值为:{}", serviceSubtask);
                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                //记录状态
                setFailPreachForm(serviceSubtask, "3", "静默次数达到" + num + "次", "4");
                Map<String, String> map = delRedisValue(null, id.toString());
                if (ObjectUtils.isNotEmpty(map)) redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                //在redis中保存一下结束语,在调用挂电话的方法时删除
                ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
            }
            return phoneCallBackYQVO;
        } else if ("AsrCallback".equals(phoneCallReqYQVO.getOperate()) && phoneCallReqYQVO.getEnd_time() != null && phoneCallReqYQVO.getEnd_time() != -1) {
            //做一个标识,用来在通话详情接口里,判断是否有人接电话(value=2,表示有人接)
            redisCache.deleteObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup");
            redisCache.setCacheObject(phoneCallReqYQVO.getTaskid() + "recordAccept-hungup", "2", 10, TimeUnit.MINUTES);
            // å…ˆå¯¹é€šè¯è¿›è¡Œé€šç”¨åº“匹配一次
            String extemplateID = ivrTaskTemplate.getSubmoduleID();
            if (StringUtils.isNotEmpty(extemplateID)) {
                String[] split = extemplateID.split(",");
                List<String> list = Arrays.asList(split);
                List<Long> list1 = new ArrayList<>();
                for (String str : list) {
                    list1.add(Long.valueOf(str));
                }
                List<IvrLibaExtemplatescript> ivrLibaExtemplatescripts = ivrLibaExtemplatescriptMapper.queryIvrLibaExtemplatescriptList(list1);
                IvrLibaExtemplatescript les = null;
                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());
                        phoneCallBackYQVO.setCommonValue(ivrLibaExtemplatescript.getSwitchText());
                        les = ivrLibaExtemplatescript;
                    }
                    break;
                }
                log.info("是否需要挂机:{}", les);
                if (ObjectUtils.isNotEmpty(les)) {
                    //对匹配的结果处理一下(看看是不是需要挂机)
                    if (les.getIsEnd() != null && les.getIsEnd() == 1) {
                        ServiceSubtask ss = new ServiceSubtask();
                        ss.setId(serviceSubtask.getId());
                        ss.setSendstate(6L);
                        ss.setRemark("电话拨打已完成");
                        serviceSubtaskMapper.updateServiceSubtask(ss);
                        //记录状态
                        setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                        //可以挂机了
                        redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                        //在redis中保存一下结束语,在调用挂电话的方法时删除
                        ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                        redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                        phoneCallBackYQVO.setType("text");
                        phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + les.getSwitchText());
                        //将结果写进detail表
                        ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                        List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                        ivrTaskTemplateScriptVO.setExtemplateText(les.getSwitchText());
                        serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate));
                        serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                        serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                        serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                        saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                        return phoneCallBackYQVO;
                    }
                }
            }
            //如果是文本回调
            //根据问题ID获取该问题的类型
            if (ivrTaskTemplateScriptVO.getScriptType().equals("1")) {
                //用来标记,是否有匹配上的
                Integer flag = 0;
                //1-AI识别
                String url = configService.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());
                    if (aiMatchResult != null) {
                        return aiMatchResult;
                    }
                } else {
                    //是选择题
                    for (int j = 0; j < ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().size(); j++) {
                        log.error("phoneCallReqYQVO.getAsrtext()的值为:{}", phoneCallReqYQVO.getAsrtext());
                        if (StringUtils.isEmpty(phoneCallReqYQVO.getAsrtext())) {
                            continue;
                        }
                        boolean matchedFlag = false;
                        //包含
                        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()) {
                            matchedFlag = true;
                        }
                        //说明匹配正确了
                        if (matchedFlag) {
                            //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法
                            log.info("匹配正确了吗--------------");
                            ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).setIsUserOperation(1);
                            serviceTaskScriptTargetoptionMapper.updateIvrTaskTemplateTargetoption(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j));
                            //将患者的回签写进service_subtask_detail中
                            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                            ivrTaskTemplateScriptVO.setQuestionResult(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getOptiondesc());
                            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
                            //修改一下语音路径(改成前端可以访问的,存到数据库中)
                            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                                String[] split = questionvoice.split("\\\\");
                                if (split.length > 0) {
                                    String lastPart = split[split.length - 1];
                                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                                }
                            }
                            serviceSubtaskDetailList.add(serviceSubtaskDetail);
                            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                            serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                            serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                            //判断一下当前的问题是不是满意度问题,并且dutyDeptCode是否有值,并且选项是不是异常选项,如果全符合,则往满意度问题异常表新增
                            if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getDutyDeptCode()) && 1 == ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getIsabnormal()) {
                                ServiceSubtaskDetailTrace subtaskDetailTrace = new ServiceSubtaskDetailTrace();
                                //用taskid、subid和scriptid去获取detailid
                                ServiceSubtaskDetail ssd = new ServiceSubtaskDetail();
                                ssd.setSubId(serviceSubtask.getId());
                                ssd.setTaskid(serviceSubtask.getTaskid());
                                ssd.setScriptid(StringUtils.isNotEmpty(scriptId) ? Long.valueOf(scriptId) : null);
                                List<ServiceSubtaskDetail> serviceSubtaskDetails = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(ssd);
                                subtaskDetailTrace.setDetailId(CollectionUtils.isNotEmpty(serviceSubtaskDetails) ? serviceSubtaskDetails.get(0).getId() : null);
                                subtaskDetailTrace.setSubId(serviceSubtask.getId());
                                subtaskDetailTrace.setTaskid(serviceSubtask.getTaskid());
                                subtaskDetailTrace.setTemplateid(ivrTaskTemplateScriptVO.getId());
                                subtaskDetailTrace.setTemplatequestionnum(ivrTaskTemplateScriptVO.getScriptno());
                                subtaskDetailTrace.setSwitchid(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getId());
                                subtaskDetailTrace.setQuestiontext(ivrTaskTemplateScriptVO.getScriptContent());
                                subtaskDetailTrace.setQuestionvoice(null);
                                subtaskDetailTrace.setCategoryname(ivrTaskTemplateScriptVO.getScriptAssortname());
                                subtaskDetailTrace.setCategoryid(ivrTaskTemplateScriptVO.getScriptAssortid());
                                //获取所有选项
                                String optionDescStr = Optional.ofNullable(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()).filter(list -> !list.isEmpty()).map(list -> list.stream().map(IvrTaskTemplateTargetoption::getOptiondesc).filter(Objects::nonNull).collect(Collectors.joining("&"))).orElse("");
                                subtaskDetailTrace.setTargetid(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getId());
                                subtaskDetailTrace.setTargetvalue(optionDescStr);
                                subtaskDetailTrace.setMatchedtext(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getOptiondesc());
                                subtaskDetailTrace.setValueType(serviceSubtaskDetails.get(0).getValueType());
                                subtaskDetailTrace.setTemplateType(1);
                                SvyLibScriptCategory svyLibScriptCategory = svyLibScriptCategoryMapper.selectSvyLibScriptCategoryById(ivrTaskTemplateScriptVO.getScriptAssortid());
                                subtaskDetailTrace.setType(svyLibScriptCategory.getType());
                                subtaskDetailTrace.setScriptid(ivrTaskTemplateScriptVO.getId());
                                subtaskDetailTrace.setAsrtext(phoneCallReqYQVO.getAsrtext());
                                subtaskDetailTrace.setRecordpath(phoneCallReqYQVO.getRecordpath());
                                subtaskDetailTrace.setPatid(serviceSubtask.getPatid());
                                JSONObject patdescJson = new JSONObject();
                                patdescJson.put("sendname", serviceSubtask.getSendname());
                                patdescJson.put("phone", serviceSubtask.getPhone());
                                patdescJson.put("age", serviceSubtask.getAge());
                                patdescJson.put("sex", serviceSubtask.getSex() != null ? serviceSubtask.getSex() == 1 ? "男" : "女" : null);
                                subtaskDetailTrace.setPatdesc(patdescJson.toJSONString());
                                subtaskDetailTrace.setTodeptcode(ivrTaskTemplateScriptVO.getDutyDeptCode());
                                subtaskDetailTrace.setTodeptname(ivrTaskTemplateScriptVO.getDutyDeptName());
                                subtaskDetailTrace.setOrgid(serviceSubtask.getOrgid());
                                subtaskDetailTrace.setHandleFlag("0");
                                subtaskDetailTrace.setGuid(phoneCallReqYQVO.getGuid());
                                subtaskDetailTrace.setCreateTime(new Date());
                                subtaskDetailTrace.setUpdateTime(new Date());
                                subtaskDetailTrace.setOrgid(phoneCallReqYQVO.getOrgid());
                                traceService.insertServiceSubtaskDetailTtrace(subtaskDetailTrace);
                            }
                            //判断一下,这个选项结果是不是还有继续问下去的必要,例如选项结果是别人不想继续回答问题,就要结束掉
                            if (ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getIsEnd() == 1) {
                                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                                //在redis中保存一下结束语,在调用挂电话的方法时删除
                                ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                                phoneCallBackYQVO.setType("text");
                                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                                //记录状态
                                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                                return phoneCallBackYQVO;
                            }
                            flag = 1;
                            //将当前前的播报状态删除,给下一题让位
                            redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
//                        //获取下一题
                            log.error("获取下一题的信息:{}", ivrTaskTemplateScriptVO);
                            if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1") || ivrTaskTemplateScriptVO.getBranchFlag().equals("0") && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) {
                                Long nextQuestion = null;
                                if (ivrTaskTemplateScriptVO.getBranchFlag().equals("1")) {
                                    nextQuestion = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getNextQuestion();
                                    //更新分数
                                    double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    score = BigDecimal.valueOf(score).add(new BigDecimal(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getScore())).doubleValue();
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score);
                                } else {
                                    nextQuestion = ivrTaskTemplateScriptVO.getNextScriptno();
                                    //更新分数
                                    Object obj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    Double score = (obj == null ? new Double(0.00) : new Double(((Double) obj).doubleValue()));
                                    score = BigDecimal.valueOf(score).add(new BigDecimal(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(j).getScore())).doubleValue();
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                                }
                                if (nextQuestion == null || nextQuestion == 0L) {
                                    //如果下一题为空,或者为0,则挂机
                                    ServiceSubtask ss = new ServiceSubtask();
                                    ss.setId(serviceSubtask.getId());
                                    ss.setSendstate(6L);
                                    ss.setRemark("电话拨打已完成");
                                    serviceSubtaskMapper.updateServiceSubtask(ss);
                                    //记录状态
                                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                                    redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                                    ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                                    phoneCallBackYQVO.setType("text");
                                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                                    //将结果写进detail表
                                    ServiceSubTaskDetailReq ssdReq = new ServiceSubTaskDetailReq();
                                    List<ServiceSubtaskDetail> serviceSubtaskDetailList2 = new ArrayList<>();
                                    serviceSubtaskDetailList.add(getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate));
                                    serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList2);
                                    ssdReq.setGuid(phoneCallReqYQVO.getGuid());
                                    ssdReq.setOrgid(phoneCallReqYQVO.getOrgid());
                                    saveQuestionAnswerPhone(ssdReq);
                                    return phoneCallBackYQVO;
                                }
                                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                                    if (script.getSort() == nextQuestion.intValue()) {
                                        phoneCallBackYQVO.setType("text");
                                        phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                                        String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                                        log.error("下一题问题:{}", scriptContent);
                                        log.error("下一题的子任务是:{}", serviceSubtask);
                                        phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + 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.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                                ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                                phoneCallBackYQVO.setType("text");
                                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                                //更新一下分数
                                double score = 0.0;
                                Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                if (ObjectUtils.isNotEmpty(scoreObj)) score = (double) scoreObj;
                                serviceSubtask.setScore(String.valueOf(score));
                                serviceSubtask.setFinishtime(new Date());
                                serviceSubtask.setSendstate(6L);
                                serviceSubtask.setRemark("电话拨打已完成");
                                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                                //记录状态
                                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                                //设置结束语
                                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                                Long id = serviceSubtask.getId();
                                Map<String, String> map = delRedisValue(null, id.toString());
                                log.error("map的值为:{}", map);
                                if (ObjectUtils.isNotEmpty(map))
                                    redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                                //在redis中保存一下结束语,在调用挂电话的方法时删除
                                ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
//                                return phoneCallBackYQVO;
                            }
                        } else {
                            log.info("没有匹配上----------------------------");
                            //flag=0,说明没 åŒ¹é…ä¸Šï¼Œä¹Ÿè¦æŠŠæ‚£è€…说的话记录下来
                            if (j == ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().size() - 1 && flag == 0) {
                                ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                                List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                                ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
                                //修改一下语音路径(改成前端可以访问的,存到数据库中)
                                if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                                    String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                                    String[] split = questionvoice.split("\\\\");
                                    if (split.length > 0) {
                                        String lastPart = split[split.length - 1];
                                        serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                                    }
                                }
                                serviceSubtaskDetailList.add(serviceSubtaskDetail);
                                //如果没有 åŒ¹é…ä¸Šï¼Œè¿™ä¸ªå¿…须为null
                                serviceSubtaskDetailList.get(0).setMatchedtext("");
                                serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                                serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                                serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                                saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                            }
                            continue;
                        }
                    }
                }
                //都没有匹配到
                if (StringUtils.isEmpty(phoneCallBackYQVO.getValue())) {
                    log.info("都没有匹配到-------------------------");
                    Integer count = null;
                    Object countObj = redisCache.getCacheObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                    if (ObjectUtils.isNotEmpty(countObj)) count = (Integer) countObj;
                    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) {
                            //如果是最后一道题,或者没有下一题了,就直接挂机
                            ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                            phoneCallBackYQVO.setType("text");
                            phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                            if (StringUtils.isNotEmpty(phoneCallBackYQVO.getValue()))
                                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + phoneCallBackYQVO.getValue() + serviceTask1.getJsy());
                            else phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                            //更新一下分数
                            Double score = null;
                            Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                            serviceSubtask.setScore(String.valueOf(score));
                            serviceSubtask.setFinishtime(new Date());
                            serviceSubtask.setSendstate(6L);
                            serviceSubtask.setRemark("电话拨打已完成");
                            serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                            //记录状态
                            setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                            //在redis中保存一下结束语,在调用挂电话的方法时删除
                            ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                            redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                            //去redis中,把该子任务ID删除
                            Long id = serviceSubtask.getId();
                            Map<String, String> map = delRedisValue(null, id.toString());
                            if (ObjectUtils.isNotEmpty(map)) {
                                redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            }
//                            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 = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent));
                                    //将该患者的Redis中的题目ID,进行修改
                                    redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES);
                                    //更新一下分数
                                    Double score = null;
                                    Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                                    if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                                    score = BigDecimal.valueOf(score).add(new BigDecimal(script.getScore())).doubleValue();
                                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 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, StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent()));
                    }
                }
            } else {
                //不是选择题,直接记录答案,将结果写到detail中
                ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
                List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
                ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
                //修改一下语音路径(改成前端可以访问的,存到数据库中)
                if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                    String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                    String[] split = questionvoice.split("\\\\");
                    if (split.length > 0) {
                        String lastPart = split[split.length - 1];
                        serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                    }
                }
                serviceSubtaskDetailList.add(serviceSubtaskDetail);
                serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
                serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
                serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
                saveQuestionAnswerPhone(serviceSubTaskDetailReq);
                //    String xh = idSort.split("-")[1];
                //如果选项分支为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 = StringUtils.isNotEmpty(ivrTaskTemplateScript1.getIvrtext()) ? ivrTaskTemplateScript1.getIvrtext() : ivrTaskTemplateScript1.getScriptContent();
                            phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent));
                            redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                            redisCache.setCacheObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone(), ivrTaskTemplateScript1.getId().toString());
                            //更新一下分数
                            Double score = null;
                            Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                            score = BigDecimal.valueOf(score).add(new BigDecimal(ivrTaskTemplateScriptVO.getScore())).doubleValue();
                            redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                        }
                    }
                } else if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                    //没有下一题了,就结束了
                    ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                    //更新一下分数
                    Double score = null;
                    Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                    serviceSubtask.setScore(String.valueOf(score));
                    serviceSubtask.setFinishtime(new Date());
                    serviceSubtask.setRemark("电话拨打已完成");
                    serviceSubtask.setSendstate(6L);
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    //记录状态
                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                    //去redis中,把该子任务ID删除
                    Long id = serviceSubtask.getId();
                    Map<String, String> map = delRedisValue(null, id.toString());
                    redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask1.getJsy(), 120, TimeUnit.MINUTES);
                    redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                }
            }
        }
        log.error("phoneCallBackYQVO的返回值是:{}", phoneCallBackYQVO);
        return phoneCallBackYQVO;
    }
    private String getObject(ServiceSubtask serviceSubtask, String scriptContent) {
        Map<String, Map<String, String>> param = getParam(serviceSubtask.getTaskid());
        for (Map<String, String> map : param.values()) {
            for (String key : map.keySet()) {
                scriptContent = scriptContent.replace(key, StringUtils.isNotEmpty(map.get(key)) ? map.get(key) : "");
            }
        }
        if (StringUtils.isEmpty(scriptContent)) {
            return scriptContent;
        }
        scriptContent = scriptContent.replace("${name}", StringUtils.isNotEmpty(serviceSubtask.getSendname()) ? serviceSubtask.getSendname() : "");
        scriptContent = scriptContent.replace("${dzz}", StringUtils.isNotEmpty(serviceSubtask.getAddr()) ? serviceSubtask.getAddr() : "");
        scriptContent = scriptContent.replace("${phone}", StringUtils.isNotEmpty(serviceSubtask.getTelcode()) ? serviceSubtask.getTelcode() : "");
        return scriptContent;
    }
    /**
     * èŽ·å–ä»»åŠ¡é‡Œçš„é€šé…ç¬¦
     *
     * @param taskId
     * @return
     */
    private Map<String, Map<String, String>> getParam(Long taskId) {
        ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(taskId);
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Map<String, String>> serviceTaskMap = null;
        try {
            serviceTaskMap = objectMapper.readValue(serviceTask.getTextParam(), Map.class);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
        return serviceTaskMap;
    }
    private Map<String, String> delRedisValue(String cache, String id) {
        id = "," + id + ",";
        String val = "";
        String cacheName = "";
        Map<String, String> 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<PullTaskVO> taskPull() {
        //pullTaskVOList用于数据返回
        List<PullTaskVO> pullTaskVOList = new ArrayList<>();
        List<String> cacheList = new ArrayList<>();
        ListOperations<String, String> listOps = redisTemplate.opsForList();
        //必须等ache-exist全部打完才能拉取下一批(主要是怕所有的数据都堆在电话的缓存里,在规定的时间打不完)
//        long existSize = listOps.size("cache-exist");
//        if (existSize > 0) return pullTaskVOList;
        int count = 0;
        long size = listOps.size("cache-0");
        log.info("---------打电话之前缓存中的数据量:{}", size);
        if (size > 0) {
            //phoneCount()  é€šè¿‡ç»™å‡ºçš„æ¯ä¸ªå°æ—¶éœ€è¦æ‰“出的电话量,算出到晚上8点一点能打出多少个电话,获取到总量
            //如果需要立即执行的数据量大于一天要执行的上限,则只取上限的数量,其它的则放回redis中
            count = Math.min(phoneCount(), (int) size);
            if (count != 0) {
                cacheList = listOps.range("cache-0", 0, count - 1);
            } else {
                return null;
            }
        }
        log.info("任务拉取cache-0的值是多少:{}", cacheList);
        //  cache-0为立即发起的,其它的先推迟
        if (!CollectionUtils.isEmpty(cacheList)) {
            pullTaskVOList = getPullTaskList(cacheList, "cache-0");
            // åˆ é™¤å·²å¤„理的数据
            listOps.trim("cache-0", count, -1);
            log.info("打电话之后缓存中的数据量---------:{}", listOps.size("cache-0"));
        } else {
            for (int i = 1; i < 6; i++) {
                //取出从cache-1 åˆ° cache-5的第一个子任务信息
                try {
                    Long size1 = listOps.size("cache-" + i);
                    if (size1 == null || size1 == 0) continue;
                    int count1 = Math.min(phoneCount(), size1.intValue());
                    List<String> value = listOps.range("cache-0" + i, 0, count1 - 1);
                    List<PullTaskVO> pullTaskVOList2 = getPullTaskList(value, "cache-" + i);
                    if (CollectionUtils.isNotEmpty(pullTaskVOList2) && pullTaskVOList2.size() > 0) {
                        pullTaskVOList.addAll(pullTaskVOList2);
                    }
                    //将处理过的数据删除
                    listOps.trim("cache-0" + i, count1, -1);
                } catch (Exception e) {
//                    log.error("看着烦");
                }
            }
        }
        //existList用来记录接下来需要执行的子任务ID
//        List<String> existList = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(pullTaskVOList)) {
            //给回调参数赋值
            for (PullTaskVO pullTaskVO : pullTaskVOList) {
//                existList.add(pullTaskVO.getTaskid());
                pullTaskVO.setAsrcallback(ASRCallBackPath);
            }
        }
//        if (existList.size() != 0) redisCache.setCacheListRight("cache-exist", existList);
        log.info("-----------实际拉取的电话数据量:{}", CollectionUtils.isNotEmpty(pullTaskVOList) ? pullTaskVOList.size() : null);
        return pullTaskVOList;
    }
    private List<PullTaskVO> getPullTaskList(List<String> subIds, String cacheName) {
        log.error("任务拉取subIds的值是多少:{}", subIds);
        //pullTaskVOList用于数据返回
        List<PullTaskVO> pullTaskVOList = new ArrayList<>();
        //newValue0用于保存没有处理的子 ä»»åŠ¡
//        String newValue0 = "";
        List<String> newValue0 = new ArrayList<>();
        //根据,获取子任务的ID
//        String[] split = subIds.split(",,");
        for (int i = 0; i < subIds.size(); i++) {
            if (cacheName.equals("cache-0") && i < subIds.size() || !cacheName.equals("cache-0") && i < 1) {
                PullTaskVO pullTaskVO = new PullTaskVO();
                String subId = subIds.get(i).trim();
                ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(subId));
                if (ObjectUtils.isEmpty(serviceSubtask)) {
                    log.error("该subId有问题,在表中查询不到:{}", subId);
                    continue;
                }
                if (StringUtils.isEmpty(serviceSubtask.getPhone())) {
                    serviceSubtask.setRemark("手机号为空");
                    serviceSubtask.setSendstate(4L);
                    serviceSubtask.setResult("error");
//                    serviceSubtask.setFinishtime(new Date());
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    continue;
                }
                ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
                if (ObjectUtils.isEmpty(ivrTaskTemplate)) continue;
                //通过任务模板中的"第一次问题编号"获取第一个问题;
                IvrTaskTemplateScript ivrTaskTemplateScript = null;
                IvrTaskTemplateScript ivrTaskTemplateScript1 = new IvrTaskTemplateScript();
                ivrTaskTemplateScript1.setTemplateID(ivrTaskTemplate.getId());
                List<IvrTaskTemplateScript> 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)) {
                    log.error("---------这个ivrTaskTemplate.getId()的问题为空:{}", ivrTaskTemplate.getId());
                    continue;
                }
                //获取通配符匹配过后的问题
                String scriptContent = ivrTaskTemplateScript.getIvrtext();
                if (StringUtils.isEmpty(scriptContent)) scriptContent = ivrTaskTemplateScript.getScriptContent();
                String scrContent = getObject(serviceSubtask, scriptContent);
                String kcb = serviceTask.getKcb() + "," + scrContent;
                //封装返回数据
                //taskId = å­ä»»åŠ¡ID + é—®é¢˜ID +问题序号
                pullTaskVO.setTaskid(subId);
                pullTaskVO.setAppkey(app_key_yq);
                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);
                if (cacheName.equals("cache-0")) pullTaskVO.setDisplayNo("2");
                else pullTaskVO.setDisplayNo("2");
                pullTaskVOList.add(pullTaskVO);
                redisCache.setCacheObject(subId.trim() + "-" + serviceSubtask.getPhone().trim(), ivrTaskTemplateScript.getId().toString(), 120, TimeUnit.MINUTES);
            } else {
                newValue0.add(subIds.get(i));
                redisCache.setCacheListRight(cacheName, newValue0);
            }
        }
        return pullTaskVOList;
    }
    @Override
    public Integer saveQuestionAnswerPhone(ServiceSubTaskDetailReq serviceSubTaskDetailReq) {
        int i = 0;
        log.info("保存电话回调内容 saveQuestionAnswerPhone:{}", serviceSubTaskDetailReq);
        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));
            ServiceSubtaskEntity ivrTaskSingle = new ServiceSubtaskEntity();
            ivrTaskSingle.setTaskid(tid);
            ivrTaskSingle.setPatid(pid);
            List<ServiceSubtask> selectServiceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(ivrTaskSingle);
            if (CollectionUtils.isEmpty(selectServiceSubtaskList) || selectServiceSubtaskList.size() == 0) {
                log.error("报错了,selectServiceSubtaskList数据为空了:{}", ivrTaskSingle);
                return 0;
            }
            //随访
            for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubTaskDetailReq.getServiceSubtaskDetailList()) {
                if (serviceSubtaskDetail.getScriptid() != null) {
                    //设置一下问题分类
                    IvrTaskTemplateScript ivrTaskTemplateScript = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptByID(serviceSubtaskDetail.getScriptid());
                    if (ivrTaskTemplateScript != null)
                        serviceSubtaskDetail.setCategoryid(ivrTaskTemplateScript.getScriptAssortid());
                    serviceSubtaskDetail.setIsMydException(ivrTaskTemplateScript.getIsMydException());
                    serviceSubtaskDetail.setDimension(ivrTaskTemplateScript.getDimension());
                }
                serviceSubtaskDetail.setSubId(selectServiceSubtaskList.get(0).getId());
//                serviceSubtaskDetail.setId(UUID.randomUUID().toString());
                serviceSubtaskDetail.setCreateTime(new Date());
                serviceSubtaskDetail.setGuid(selectServiceSubtaskList.get(0).getGuid());
                serviceSubtaskDetail.setOrgid(serviceSubTaskDetailReq.getOrgid());
                serviceSubtaskDetail.setGuid(serviceSubTaskDetailReq.getGuid());
                i = serviceSubtaskDetailMapper.insertServiceSubtaskDetail(serviceSubtaskDetail);
            }
        } else {
            if (serviceSubTaskDetailReq != null && CollectionUtils.isNotEmpty(serviceSubTaskDetailReq.getServiceSubtaskDetailList())) {
                for (ServiceSubtaskDetail serviceSubtaskDetail : serviceSubTaskDetailReq.getServiceSubtaskDetailList()) {
                    if (serviceSubtaskDetail.getScriptid() != null) {
                        //设置一下问题分类
                        IvrTaskTemplateScript ivrTaskTemplateScript = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptByID(serviceSubtaskDetail.getScriptid());
                        if (ivrTaskTemplateScript != null)
                            serviceSubtaskDetail.setCategoryid(ivrTaskTemplateScript.getScriptAssortid());
                    }
                    serviceSubtaskDetail.setCreateTime(new Date());
                    serviceSubtaskDetail.setGuid(serviceSubTaskDetailReq.getGuid());
                    serviceSubtaskDetail.setOrgid(serviceSubTaskDetailReq.getOrgid());
                    //先根据子任务id,任务ID和问题序号查询一下,该问题是否已经有值,如果有值,直接修改,如果为空,直接插入
                    ServiceSubtaskDetail ssd = new ServiceSubtaskDetail();
                    ssd.setSubId(serviceSubtaskDetail.getSubId());
                    ssd.setTaskid(serviceSubtaskDetail.getTaskid());
                    ssd.setScriptid(serviceSubtaskDetail.getScriptid());
                    if (serviceSubtaskDetail.getSubId() != null) {
                        ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(serviceSubtaskDetail.getSubId());
                        ssd.setGuid(serviceSubtask.getGuid());
                        ssd.setOrgid(serviceSubtask.getOrgid());
                    }
                    List<ServiceSubtaskDetail> serviceSubtaskDetails = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(ssd);
                    if (CollectionUtils.isNotEmpty(serviceSubtaskDetails) && StringUtils.isNotEmpty(serviceSubtaskDetails.get(0).getMatchedtext())) {
                        serviceSubtaskDetail.setId(serviceSubtaskDetails.get(0).getId());
                        serviceSubtaskDetailMapper.updateServiceSubtaskDetail(serviceSubtaskDetail);
                    } else {
                        if (serviceSubtaskDetail.getSubId() != null) {
                            ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(serviceSubtaskDetail.getSubId());
                            serviceSubtaskDetail.setGuid(serviceSubtask.getGuid());
                            serviceSubtaskDetail.setOrgid(serviceSubtask.getOrgid());
                        }
                        i = serviceSubtaskDetailMapper.insertServiceSubtaskDetail(serviceSubtaskDetail);
                    }
                }
            }
        }
        return i;
    }
    @Override
    public Integer recordAccept(PhoneCallRecordVO phoneCallRecordVO) {
        Object cacheObject = redisCache.getCacheObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
        if (phoneCallRecordVO.getHangup_cause().equals("NO_USER_RESPONSE") || ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("1")) {
            //无人接听
            //记录电话发送状态
            ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallRecordVO.getTaskid()));
            setFailPreachForm(serviceSubtask, "3", "无人接听", "4");
            //进行短信补偿
//            getSmsCompensate(Long.valueOf(phoneCallRecordVO.getTaskid()));
            redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
//            redisCache.removeElementFromList("cache-exist", phoneCallRecordVO.getTaskid());
        } else if (ObjectUtils.isNotEmpty(cacheObject) && String.valueOf(cacheObject).equals("2")) {
//        if (phoneCallRecordVO.getAnswered() != null && phoneCallRecordVO.getAnswered() == true) {
            log.error("有人接电话了,并且完了");
            //说明有人接了
            int startIndex = phoneCallRecordVO.getRecord_path().indexOf("voice") + "voice".length() + 1;  // åŠ 1是跳过/符号
            int endIndex = phoneCallRecordVO.getRecord_path().lastIndexOf("/");
            String result = phoneCallRecordVO.getRecord_path().substring(startIndex, endIndex);
            ServiceSubtask serviceSubtask = new ServiceSubtask();
            serviceSubtask.setRemark("电话拨打已完成");
            serviceSubtask.setSendstate(6L);
            serviceSubtask.setId(Long.valueOf(phoneCallRecordVO.getTaskid()));
            serviceSubtask.setFinishtime(new Date());
            serviceSubtask.setUpdateTime(new Date());
            log.error("电话拨打已完成serviceSubtask的值为:{}", serviceSubtask);
            serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
            setFailPreachForm(serviceSubtask, StringUtils.isNotEmpty(serviceSubtask.getCurrentPreachform()) ? serviceSubtask.getCurrentPreachform() : "3", "电话拨打已完成", "9");
            //下载文件
            ftpService.downloadFolder("/" + LocalDate.now().toString(), profile + "/upload/vadio/voice/" + LocalDate.now().toString());
            redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
        } else {
            //患者拒绝随访
            //先把该subId从cache_exist中剔除,只要有回调就剔除(目的是,尽快让cache-exist为空,去缓存下一批要打的电话;这里剔除不影响电话的正常业务)
//            redisCache.removeElementFromList("cache-exist", phoneCallRecordVO.getTaskid());
            ServiceSubtask serviceSubtask = new ServiceSubtask();
            serviceSubtask.setRemark("电话接通成功,患者拒绝随访");
            serviceSubtask.setId(Long.valueOf(phoneCallRecordVO.getTaskid()));
            serviceSubtask.setSendstate(6L);
            serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
//            int startIndex = phoneCallRecordVO.getRecord_path().indexOf("voice") + "voice".length() + 1;  // åŠ 1是跳过/符号
//            int endIndex = phoneCallRecordVO.getRecord_path().lastIndexOf("/");
//            String result = phoneCallRecordVO.getRecord_path().substring(startIndex, endIndex);
            //下载文件
//            ftpService.downloadFolder("/" + LocalDate.now().toString(), profile + "/upload/vadio/voice/" + LocalDate.now().toString());
            redisCache.deleteObject(phoneCallRecordVO.getTaskid() + "recordAccept-hungup");
            ServiceSubtask ss = serviceSubtaskMapper.selectServiceSubtaskById(serviceSubtask.getId());
            setFailPreachForm(ss, StringUtils.isNotEmpty(ss.getCurrentPreachform()) ? ss.getCurrentPreachform() : "3", "电话接通成功,患者拒绝随访", "5");
        }
        return 1;
    }
    @Override
@@ -4078,163 +2838,6 @@
        return serviceSubtaskStatistic;
    }
    private IvrTaskTemplateScriptVO getNextQuestion(List<IvrTaskTemplateScriptVO> 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) {
        log.info("-----------------phoneCallReqYQVO的值为:{}", phoneCallReqYQVO);
        ServiceSubtaskDetail serviceSubtaskDetail = new ServiceSubtaskDetail();
        serviceSubtaskDetail.setSubId(Long.valueOf(phoneCallReqYQVO.getTaskid()));
        ServiceSubtask serviceSubtask1 = serviceSubtaskMapper.selectServiceSubtaskById(Long.valueOf(phoneCallReqYQVO.getTaskid()));
        if (serviceSubtask1 != null) {
            serviceSubtaskDetail.setTaskid(serviceSubtask1.getTaskid());
            serviceSubtaskDetail.setPatid(serviceSubtask1.getPatid());
        }
        serviceSubtaskDetail.setTargetid(ivrTaskTemplateScriptVO.getTargetid() != null ? ivrTaskTemplateScriptVO.getTargetid().intValue() : null);
        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.setCategoryid(ivrTaskTemplateScriptVO.getScriptAssortid());
        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.getScriptno());
        serviceSubtaskDetail.setQuestiontext(StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent());
        serviceSubtaskDetail.setQuestionvoice(phoneCallReqYQVO.getRecordpath());
        serviceSubtaskDetail.setCategoryname(ivrTaskTemplateScriptVO.getScriptType());
        serviceSubtaskDetail.setTargetoptions(ivrTaskTemplateScriptVO.getTargetOptions());
        serviceSubtaskDetail.setExtemplateText(ivrTaskTemplateScriptVO.getExtemplateText());
        serviceSubtaskDetail.setScriptid(ivrTaskTemplateScriptVO.getId());
        serviceSubtaskDetail.setLibTemplateid(serviceSubtask.getLibtemplateid() != null ? serviceSubtask.getLibtemplateid().intValue() : null);
        int i = 1;
        for (IvrTaskTemplateTargetoption ivrTaskTemplateTargetoption : ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()) {
            if (ivrTaskTemplateTargetoption.getIsUserOperation() == 1) {
                serviceSubtaskDetail.setMatchedtext(ivrTaskTemplateTargetoption.getTargetvalue());
                serviceSubtaskDetail.setScore(ivrTaskTemplateTargetoption.getScore() != null ? ivrTaskTemplateTargetoption.getScore().toString() : "0");
            }
            serviceSubtaskDetail.setTargetvalue(StringUtils.isEmpty(serviceSubtaskDetail.getTargetvalue()) ? ivrTaskTemplateTargetoption.getTargetvalue() : serviceSubtaskDetail.getTargetvalue() + "&" + ivrTaskTemplateTargetoption.getTargetvalue());
        }
        serviceSubtaskDetail.setAddtime(new Date());
        serviceSubtaskDetail.setIsupload(0L);
        serviceSubtaskDetail.setUploadTime(new Date());
        serviceSubtaskDetail.setDelFlag("0");
        serviceSubtaskDetail.setValueType(ivrTaskTemplateScriptVO.getScriptType());
        return serviceSubtaskDetail;
    }
    /**
     * ç”µè¯çš„补偿方法(老)
     *
     * @param subTaskId
     */
    private void getSmsCompensate(Long subTaskId) {
        ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(subTaskId);
        RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample();
        String taskId = rsaPublicKeyExample.encryptedData(serviceSubtask.getTaskid().toString(), pub_key);
        String patid = rsaPublicKeyExample.encryptedData(serviceSubtask.getPatid().toString(), pub_key);
        String subId = rsaPublicKeyExample.encryptedData(serviceSubtask.getId().toString(), pub_key);
        ServiceOutPath serviceOutPath = new ServiceOutPath();
        serviceOutPath.setParam1(taskId);
        serviceOutPath.setParam2(patid);
        serviceOutPath.setCreateTime(new Date());
        serviceOutPath.setParam6(subId);
        serviceOutPath.setOrgid(serviceSubtask.getOrgid());
        iServiceOutPathService.insertServiceOutPath(serviceOutPath);
        //转成16进制
        String format = String.format("%03X", serviceOutPath.getId());
        String url = "";
        SendMagParam sendMagParam = new SendMagParam();
        url = localIP + ":" + req_path + "/sf?p=" + format;
        sendMagParam.setUrl(url);
        sendMagParam.setContent("【新华医院】您好,邀请您填写出院随访调查表,请点击" + sendMagParam.getUrl() + "填写。感谢您配合!");
        serviceOutPath.setRadix(format);
        serviceOutPath.setUpdateTime(new Date());
        serviceOutPath.setUrl(url);
        iServiceOutPathService.updateServiceOutPath(serviceOutPath);
        //短信发送已收口到 ruoyi-message æ¨¡å—;与原实现一致:不判断发送结果
        smsSenderFactory.send(active, SmsSendRequest.of(serviceSubtask.getPhone(), sendMagParam.getContent(), serviceSubtask.getOrgid()));
        redisCache.setCacheObject(subTaskId + "recordAccept-hungup", "1", 10, TimeUnit.MINUTES);
        if (StringUtils.isNotEmpty(serviceSubtask.getRemark()))
            serviceSubtask.setRemark("电话发送拒接,短信补偿发送成功");
        else serviceSubtask.setRemark("短信补偿发送成功");
        serviceSubtask.setSendstate(3L);
        serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
        //需要在servicePreachform里记录一下短信状态
        ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
        serviceSubtaskPreachform.setPreachform("4");
        serviceSubtaskPreachform.setSubid(subTaskId);
        serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
        serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
        List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
            ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0);
            serviceSubtaskPreachform1.setSendstate("9");
            serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1);
        } else if (CollectionUtils.isEmpty(serviceSubtaskPreachforms)) {
            //如果为空的话,说明用户没有选短信发送方式,这个时候需要强制新增一条
            //获取sort的最大值
            ServiceSubtaskPreachform ssp = new ServiceSubtaskPreachform();
            ssp.setSubid(subTaskId);
            ssp.setTaskid(serviceSubtask.getTaskid());
            ssp.setOrgid(serviceSubtask.getOrgid());
            List<ServiceSubtaskPreachform> sspf2 = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(ssp);
            serviceSubtaskPreachform.setSort(0L);
            if (CollectionUtils.isNotEmpty(sspf2)) {
                serviceSubtaskPreachform.setSort(Long.valueOf(sspf2.size()));
            }
            serviceSubtaskPreachform.setSendstate("2");
            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(serviceSubtaskPreachform);
        }
    }
    //通过当前时间,拉取电话总数
    private Integer phoneCount() {
        // å½“前时间
        LocalTime currentTime = LocalTime.now();
        // ç›®æ ‡æ—¶é—´ï¼ˆæ™šä¸Š 8 ç‚¹ï¼‰
        LocalTime targetTime = LocalTime.of(phoneEndHour, 0);
//        // è®¡ç®—当前时间到目标时间的时长
//        Duration duration = Duration.between(currentTime, targetTime);
//        // èŽ·å–å‰©ä½™æ—¶é—´çš„å°æ—¶æ•°
//        long remainingHours = 0;
//        long remainingMinutes = duration.toMinutes();
//        //如果超过8点,就设置为0
//        //距离结束时间小于20分钟,就不拉取了
//        if (remainingMinutes < 20) remainingHours = 0;
//        else if (remainingMinutes < 60 && remainingMinutes > 20) remainingHours = 1;
//        else if (remainingMinutes > 60) remainingHours = remainingMinutes / 60;
//        // è®¡ç®—剩余可以打的电话数
//        int remainingCalls = (int) (remainingHours * phoneUpEveryHour);
//        return remainingCalls;
        if (currentTime.isBefore(targetTime)) {
            //如果当前时间还在截止时间内,则继续发
            return phoneUpEveryHour;
        }
        return 0;
    }
    public List<ServiceSubtaskCount> getSfStatisticsJoydetails(ServiceSubtaskCountReq serviceSubtaskCountReq) {
        return serviceSubtaskMapper.getSfStatisticsJoydetails(serviceSubtaskCountReq);
    }
@@ -4681,83 +3284,6 @@
    }
    /**
     * @param serviceSubtask
     * @param preachform
     * @param remark
     * @param failSendstate
     * @return
     */
    public Boolean setFailPreachForm(ServiceSubtask serviceSubtask, String preachform, String remark, String failSendstate) {
        log.info("setFailPreachForm éšè®¿æµç¨‹å¤„理 serviceSubtask:{}, preachform:{}, remark:{}, failSendstate:{}", serviceSubtask, preachform, remark, failSendstate);
        //将状态设置为失败
        ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
        serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
        serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid());
        serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid());
        //获取该患者的发送方式总数
        List<ServiceSubtaskPreachform> spSize = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        serviceSubtaskPreachform.setPreachform(preachform);
        //1:未发送状态
        serviceSubtaskPreachform.setSendstate("1");
        if (preachform.equals("3")) {
            //电话进入队列后,状态会变成2
            serviceSubtaskPreachform.setSendstate("2");
        }
        List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
            ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0);
            serviceSubtaskPreachform1.setSendstate(failSendstate);
            serviceSubtaskPreachform1.setRemark(remark);
            serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1);
            //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成5就行了(全失败了)
            if (serviceSubtaskPreachform1.getSort() == spSize.size() && failSendstate.equals("4")) {
                serviceSubtask.setCurrentPreachform(preachform);
                serviceSubtask.setSendstate(5L);
                serviceSubtask.setRemark(remark + ";所有执行方式均未获取结果,任务失败");
                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                return true;
            } else {
                //加入目前补偿里的 remark
                serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + remark : remark);
                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
            }
        }
        //尽管失败,这里也将当前发送方式继续设置成“电话”,sendstate设置成3;因为ryTask里的补偿的定时任务运行时,会找到这条subtask,并进行处理
        serviceSubtask.setCurrentPreachform(preachform);
        serviceSubtask.setSendstate(3L);
        //如果等于9,则说明电话正常结束了
        if (failSendstate.equals("9")) serviceSubtask.setSendstate(6L);
        serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime()));
        serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
        return true;
    }
    private Date getNextVisitTime(Long subid, Long taskid, Date visitTime) {
        ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
        serviceSubtaskPreachform.setSubid(subid);
        serviceSubtaskPreachform.setTaskid(taskid);
        List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
        if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
            //没有随访时间则按当天来算--临时
            if (ObjectUtils.isEmpty(visitTime)) {
                visitTime = new Date();
            }
            String compensateTime = serviceSubtaskPreachforms.get(0).getCompensateTime();
            int hoursToAdd = Integer.valueOf(compensateTime) * 24;
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(visitTime);
            calendar.add(Calendar.HOUR_OF_DAY, hoursToAdd);
            Date newDate = calendar.getTime();
            return newDate;
        }
        return visitTime;
    }
    /**
     * è®¡ç®—总计行
     *
     * @param statistics ç»Ÿè®¡æ•°æ®åˆ—表
@@ -4956,8 +3482,8 @@
    public void setFailSubTaskDefalutVal(Long failDay) {
        //1.先去sys_config中,获取哪些科室或病区需要默认问卷默认值
        IConfigService configService = SpringUtils.getBean(IConfigService.class);
        String deptConfigValue = configService.selectConfigByKey("hosp.default.dept");
        String wardConfigValue = configService.selectConfigByKey("hosp.default.ward");
        String deptConfigValue = taskSysConfigPort.selectConfigValue("hosp.default.dept");
        String wardConfigValue = taskSysConfigPort.selectConfigValue("hosp.default.ward");
        List<String> leaveWard = null;
        List<String> leaveDept = null;
        if (deptConfigValue != null && !deptConfigValue.isEmpty()) {
@@ -4985,7 +3511,7 @@
            Boolean flag = false;
            ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform();
            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
            List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
            List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
            if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms))
                preachformSize = serviceSubtaskPreachforms.size();
            if (serviceSubtask.getUpdateTime() != null && DateUtils.differentDaysByMillisecond(new Date(), serviceSubtask.getUpdateTime()) >= failDay) {
@@ -5114,12 +3640,13 @@
        serviceOutPath.setUpdateTime(new Date());
        serviceOutPath.setUrl(url);
        iServiceOutPathService.updateServiceOutPath(serviceOutPath);
        SysConfig sysConfig = new SysConfig();
        sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template");
        // å‚数值经端口取用(原为 sysConfigMapper.checkConfigKeyUnique + getConfigValue)。
        // è¯­ä¹‰ç­‰ä»·ï¼šåŽŸåˆ¤å®š"行存在"、这里判定"值非 null",而 sys_config.config_value æ˜¯
        // varchar(500) NOT NULL DEFAULT '' â‡’ äºŒè€…不可能不同(详见 TaskSysConfigPort javadoc)。
        String smsContemt = taskSysConfigPort.findConfigValueByKey("wj.sms.template");
        if (!Objects.isNull(sysConfig)) {
            String smsContemt = sysConfig.getConfigValue();
        if (smsContemt != null) {
            sendMagParam.setContent(smsContemt.replace("${wjurl}", sendMagParam.getUrl()));
        }
@@ -5148,7 +3675,7 @@
            //获取随访的基本信息
            ServiceSubtask serviceSubtask = selectServiceSubtaskById(subid);
            //获取患者信息
            PatArchive patArchive = patArchiveMapper.selectPatArchiveByPatid(serviceSubtask.getPatid());
            PatArchive patArchive = taskArchivePort.findByPatid(serviceSubtask.getPatid());
            RSAPublicKeyExample rsaPublicKeyExample = new RSAPublicKeyExample();
            String patid = rsaPublicKeyExample.encryptedData(serviceSubtask.getPatid().toString(), pub_key);
@@ -5485,7 +4012,7 @@
            return null;
        }
        try {
            SysDept sysDept = sysDept2Mapper.selectDeptByCode(deptCode, orgid);
            SysDept sysDept = taskSysDeptPort.selectDeptByCode(deptCode, orgid);
            return sysDept == null ? null : sysDept.getCampusid();
        } catch (Exception e) {
            log.warn("按编码反查院区失败:deptCode={}, orgid={}, err={}", deptCode, orgid, e.getMessage());
@@ -5529,460 +4056,6 @@
        }
    }
    public PhoneCallBackSHVO getFirstQues(PhoneCallReqYQVO phoneCallReqYQVO) {
        //用于返回
        PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO();
        //根据手机号获取用户的的子任务信息
        ServiceSubtaskEntity serviceSubtaskVO = new ServiceSubtaskEntity();
        serviceSubtaskVO.setPhone(phoneCallReqYQVO.getPhone());
        serviceSubtaskVO.setType("1");
        serviceSubtaskVO.setSendstate(2L);
        List<ServiceSubtask> serviceSubtasks = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO);
        if (CollectionUtils.isNotEmpty(serviceSubtasks)) {
            ServiceSubtask serviceSubtask = serviceSubtasks.get(0);
            IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
            //通过任务模板中的"第一次问题编号"获取第一个问题;
            IvrTaskTemplateScript ivrTaskTemplateScript = null;
            IvrTaskTemplateScript ivrTaskTemplateScript1 = new IvrTaskTemplateScript();
            ivrTaskTemplateScript1.setTemplateID(ivrTaskTemplate.getId());
            List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript1);
            for (IvrTaskTemplateScript ivrTaskTemplateScript2 : ivrTaskTemplateScripts) {
                if (ivrTaskTemplate.getFirstQuestionNum() == Long.valueOf(ivrTaskTemplateScript2.getSort())) {
                    ivrTaskTemplateScript = ivrTaskTemplateScript2;
                    break;
                }
            }
            //获取开场白
            ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
            phoneCallBackSHVO.setSubId(serviceSubtask.getId());
            phoneCallBackSHVO.setScriptId(ivrTaskTemplateScript.getId());
            phoneCallBackSHVO.setSort(ivrTaskTemplateScript.getSort());
            phoneCallBackSHVO.setTaskTemplateid(ivrTaskTemplateScript.getTemplateID());
            phoneCallBackSHVO.setScriptContent(serviceTask.getKcb() + "," + ivrTaskTemplateScript.getScriptContent());
        }
        return phoneCallBackSHVO;
    }
    /**
     * ä¸‰æ±‡ASR结果处理
     *
     * @param phoneCallResultYQVO
     * @return
     */
    public PhoneCallBackSHVO processResust(PhoneCallResultYQVO phoneCallResultYQVO) {
        PhoneCallBackSHVO phoneCallBackSHVO = new PhoneCallBackSHVO();
        //通过scriptid,获取所有的结果
        String url = configService.selectConfigByKey("sys.voice.match.ai.url", phoneCallResultYQVO.getOrgid());
        ServiceSubtask serviceSubtask = serviceSubtaskMapper.selectServiceSubtaskById(phoneCallResultYQVO.getSubId());
        IvrTaskTemplate ivrTaskTemplate = ivrTaskTemplateService.selectIvrTaskTemplateByID(serviceSubtask.getTemplateid());
        IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO = iIvrTaskTemplateScriptService.getTaskTempScriptInfoByid(Long.valueOf(phoneCallResultYQVO.getScriptId()));
        Integer i = matchOptionIndex(url, phoneCallResultYQVO.getScriptContent(), phoneCallResultYQVO.getAsrResult(), ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList(), phoneCallResultYQVO.getUuid());
        if (i >= 0) {
            String optiondesc = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList().get(i).getOptiondesc();
            //将患者的回答写进service_subtask_detail中
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ivrTaskTemplateScriptVO.setQuestionResult(optiondesc);
            PhoneCallReqYQVO phoneCallReqYQVO = new PhoneCallReqYQVO();
            phoneCallReqYQVO.setTaskid("" + serviceSubtask.getId());
            phoneCallReqYQVO.setPhone(phoneCallResultYQVO.getPhone());
            phoneCallReqYQVO.setAsrtext(phoneCallResultYQVO.getAsrResult());
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallResultYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallResultYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
            ivrTaskTemplateScript.setTemplateID(ivrTaskTemplateScriptVO.getTemplateID());
            List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript);
            //获取下一题
            for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                    ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                    phoneCallBackSHVO.setScriptContent(serviceTask.getJsy());
                    phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                    phoneCallBackSHVO.setSort(script.getSort());
                    phoneCallBackSHVO.setScriptId(script.getId());
                    phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                    phoneCallBackSHVO.setStatus("0");
                } else if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                    phoneCallBackSHVO.setScriptContent(script.getScriptContent());
                    phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                    phoneCallBackSHVO.setSort(script.getSort());
                    phoneCallBackSHVO.setScriptId(script.getId());
                    phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                    break;
                }
            }
            //把缓存清除
            redisCache.deleteObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
        } else {
            //再将重复询问
            //记录一下这个问题被问题的次数
            Object cacheObject = redisCache.getCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
            if (cacheObject == null || (Integer) cacheObject < ivrTaskTemplate.getMateNum()) {
                // èŽ·å–é€‰é¡¹é›†åˆä¸­çš„ optiondesc ç”¨äºŽå¼•导患者
                List<IvrTaskTemplateTargetoption> optionList = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList();
                String optionDescStr = "";
                if (optionList != null && !optionList.isEmpty()) {
                    optionDescStr = optionList.stream().map(IvrTaskTemplateTargetoption::getTargetvalue).filter(StringUtils::isNotEmpty).collect(Collectors.joining("或"));
                }
                String scriptContent = "请问" + phoneCallResultYQVO.getScriptContent();
                if (StringUtils.isNotEmpty(optionDescStr)) {
                    scriptContent += ",你可以回答:" + optionDescStr;
                }
                phoneCallBackSHVO.setScriptContent(scriptContent);
                phoneCallBackSHVO.setTaskTemplateid(phoneCallResultYQVO.getTaskTemplateid());
                phoneCallBackSHVO.setSort(phoneCallResultYQVO.getSort());
                phoneCallBackSHVO.setScriptId(phoneCallResultYQVO.getScriptId());
                phoneCallBackSHVO.setSubId(phoneCallResultYQVO.getSubId());
                if (cacheObject == null)
                    redisCache.setCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId(), 1, 120, TimeUnit.MINUTES);
                else
                    redisCache.setCacheObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId(), (Integer) cacheObject + 1, 120, TimeUnit.MINUTES);
            } else if ((Integer) cacheObject >= ivrTaskTemplate.getMateNum()) {
                //如果已经问了规定次数,就不问了,继续问下一题(这个要商量一下,是继续问下一题,还是停了,是不是要做个开关)
                IvrTaskTemplateScript ivrTaskTemplateScript = new IvrTaskTemplateScript();
                ivrTaskTemplateScript.setTemplateID(ivrTaskTemplateScriptVO.getTemplateID());
                List<IvrTaskTemplateScript> ivrTaskTemplateScripts = iIvrTaskTemplateScriptService.selectIvrTaskTemplateScriptList(ivrTaskTemplateScript);
                //获取下一题
                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                    if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                        phoneCallBackSHVO.setScriptContent(script.getScriptContent());
                        phoneCallBackSHVO.setTaskTemplateid(script.getTemplateID());
                        phoneCallBackSHVO.setSort(script.getSort());
                        phoneCallBackSHVO.setScriptId(script.getId());
                        phoneCallBackSHVO.setSubId(serviceSubtask.getId());
                        break;
                    }
                }
                //把缓存清除
                redisCache.deleteObject(phoneCallResultYQVO.getSubId() + "-" + phoneCallResultYQVO.getScriptId());
            }
        }
        return phoneCallBackSHVO;
    }
    public PhoneCallBackYQVO AiMatch(String url, PhoneCallReqYQVO phoneCallReqYQVO, PhoneCallBackYQVO phoneCallBackYQVO, IvrTaskTemplateScriptVO ivrTaskTemplateScriptVO, ServiceSubtask serviceSubtask, IvrTaskTemplate ivrTaskTemplate, List<IvrTaskTemplateScript> ivrTaskTemplateScripts, String scriptId, Integer flag, String uuid) {
        PhoneCallBackYQVO back = null;
        List<IvrTaskTemplateTargetoption> options = ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList();
        String voiceText = phoneCallReqYQVO.getAsrtext();
        String questionText = StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent();
        Integer matched = -1;
        log.info("AI匹配:url = {},questionText = {},voiceText = {},options = {},uuid ={}",
                url,questionText,voiceText,options,uuid);
        matched = matchOptionIndex(url, questionText, voiceText, options, uuid);
        //说明匹配正确了
        if (matched != null && matched >= 0) {
            IvrTaskTemplateTargetoption chosenOption = options.get(matched);
            //这里应该先判断类型,去再修改,设置IsUserOperation是单选题的改法
            log.info("匹配正确了吗--------------");
            chosenOption.setIsUserOperation(1);
            serviceTaskScriptTargetoptionMapper.updateIvrTaskTemplateTargetoption(chosenOption);
            //将患者的回签写进service_subtask_detail中
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ivrTaskTemplateScriptVO.setQuestionResult(chosenOption.getOptiondesc());
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            //判断一下当前的问题是不是满意度问题,并且dutyDeptCode是否有值,并且选项是不是异常选项,如果全符合,则往满意度问题异常表新增
            if (StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getDutyDeptCode()) && 1 == chosenOption.getIsabnormal()) {
                ServiceSubtaskDetailTrace subtaskDetailTrace = new ServiceSubtaskDetailTrace();
                //用taskid、subid和scriptid去获取detailid
                ServiceSubtaskDetail ssd = new ServiceSubtaskDetail();
                ssd.setSubId(serviceSubtask.getId());
                ssd.setTaskid(serviceSubtask.getTaskid());
                ssd.setScriptid(StringUtils.isNotEmpty(scriptId) ? Long.valueOf(scriptId) : null);
                List<ServiceSubtaskDetail> serviceSubtaskDetails = serviceSubtaskDetailMapper.selectServiceSubtaskDetailList(ssd);
                subtaskDetailTrace.setDetailId(CollectionUtils.isNotEmpty(serviceSubtaskDetails) ? serviceSubtaskDetails.get(0).getId() : null);
                subtaskDetailTrace.setSubId(serviceSubtask.getId());
                subtaskDetailTrace.setTaskid(serviceSubtask.getTaskid());
                subtaskDetailTrace.setTemplateid(ivrTaskTemplateScriptVO.getId());
                subtaskDetailTrace.setTemplatequestionnum(ivrTaskTemplateScriptVO.getScriptno());
                subtaskDetailTrace.setSwitchid(chosenOption.getId());
                subtaskDetailTrace.setQuestiontext(ivrTaskTemplateScriptVO.getScriptContent());
                subtaskDetailTrace.setQuestionvoice(null);
                subtaskDetailTrace.setCategoryname(ivrTaskTemplateScriptVO.getScriptAssortname());
                subtaskDetailTrace.setCategoryid(ivrTaskTemplateScriptVO.getScriptAssortid());
                //获取所有选项
                String optionDescStr = Optional.ofNullable(ivrTaskTemplateScriptVO.getIvrTaskScriptTargetoptionList()).filter(list -> !list.isEmpty()).map(list -> list.stream().map(IvrTaskTemplateTargetoption::getOptiondesc).filter(Objects::nonNull).collect(Collectors.joining("&"))).orElse("");
                subtaskDetailTrace.setTargetid(chosenOption.getId());
                subtaskDetailTrace.setTargetvalue(optionDescStr);
                subtaskDetailTrace.setMatchedtext(chosenOption.getOptiondesc());
                subtaskDetailTrace.setValueType(serviceSubtaskDetails.get(0).getValueType());
                subtaskDetailTrace.setTemplateType(1);
                SvyLibScriptCategory svyLibScriptCategory = svyLibScriptCategoryMapper.selectSvyLibScriptCategoryById(ivrTaskTemplateScriptVO.getScriptAssortid());
                subtaskDetailTrace.setType(svyLibScriptCategory.getType());
                subtaskDetailTrace.setScriptid(ivrTaskTemplateScriptVO.getId());
                subtaskDetailTrace.setAsrtext(phoneCallReqYQVO.getAsrtext());
                subtaskDetailTrace.setRecordpath(phoneCallReqYQVO.getRecordpath());
                subtaskDetailTrace.setPatid(serviceSubtask.getPatid());
                JSONObject patdescJson = new JSONObject();
                patdescJson.put("sendname", serviceSubtask.getSendname());
                patdescJson.put("phone", serviceSubtask.getPhone());
                patdescJson.put("age", serviceSubtask.getAge());
                patdescJson.put("sex", serviceSubtask.getSex() != null ? serviceSubtask.getSex() == 1 ? "男" : "女" : null);
                subtaskDetailTrace.setPatdesc(patdescJson.toJSONString());
                subtaskDetailTrace.setTodeptcode(ivrTaskTemplateScriptVO.getDutyDeptCode());
                subtaskDetailTrace.setTodeptname(ivrTaskTemplateScriptVO.getDutyDeptName());
                subtaskDetailTrace.setOrgid(serviceSubtask.getOrgid());
                subtaskDetailTrace.setHandleFlag("0");
                subtaskDetailTrace.setGuid(phoneCallReqYQVO.getGuid());
                subtaskDetailTrace.setCreateTime(new Date());
                subtaskDetailTrace.setUpdateTime(new Date());
                subtaskDetailTrace.setOrgid(phoneCallReqYQVO.getOrgid());
                traceService.insertServiceSubtaskDetailTtrace(subtaskDetailTrace);
            }
            //判断一下,这个选项结果是不是还有继续问下去的必要,例如选项结果是别人不想继续回答问题,就要结束掉
            if (chosenOption.getIsEnd() == 1) {
                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                //在redis中保存一下结束语,在调用挂电话的方法时删除
                ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                //记录状态
                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                back = phoneCallBackYQVO;
                return back;
            }
            flag = 1;
            //将当前前的播报状态删除,给下一题让位
            redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
//                        //获取下一题
            log.error("获取下一题的信息:{}", ivrTaskTemplateScriptVO);
            //branchFlag为null时按分支0处理,避免NPE
            if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) && ivrTaskTemplateScriptVO.getNextScriptno() != null && ivrTaskTemplateScriptVO.getNextScriptno() != 0) {
                Long nextQuestion = null;
                if ("1".equals(ivrTaskTemplateScriptVO.getBranchFlag())) {
                    nextQuestion = chosenOption.getNextQuestion();
                    //更新分数
                    double score = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    score = BigDecimal.valueOf(score).add(new BigDecimal(chosenOption.getScore())).doubleValue();
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score);
                } else {
                    nextQuestion = ivrTaskTemplateScriptVO.getNextScriptno();
                    //更新分数
                    Object obj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    Double score = (obj == null ? new Double(0.00) : new Double(((Double) obj).doubleValue()));
                    score = BigDecimal.valueOf(score).add(new BigDecimal(String.valueOf(chosenOption.getScore()))).doubleValue();
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                }
                if (nextQuestion == null || nextQuestion == 0L) {
                    //如果下一题为空,或者为0,则挂机
                    ServiceSubtask ss = new ServiceSubtask();
                    ss.setId(serviceSubtask.getId());
                    ss.setSendstate(6L);
                    ss.setRemark("电话拨打已完成");
                    serviceSubtaskMapper.updateServiceSubtask(ss);
                    //记录状态
                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                    redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                    redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "noVoice");
                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                    ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask.getJsy());
                    //明细在本题匹配成功时已落库(见上方 saveQuestionAnswerPhone),这里不再重复保存
                    back = phoneCallBackYQVO;
                    return back;
                }
                for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                    if (script.getSort() == nextQuestion.intValue()) {
                        phoneCallBackYQVO.setType("text");
                        phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                        String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                        log.error("下一题问题:{}", scriptContent);
                        log.error("下一题的子任务是:{}", serviceSubtask);
                        phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + 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());
                    }
                }
                //下一题脚本没找到时,兜底重问当前题,避免调用方误判为未处理而退回正则识别
                if (StringUtils.isEmpty(phoneCallBackYQVO.getValue())) {
                    log.error("下一题脚本未找到,重问当前题,nextQuestion:{}", nextQuestion);
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, questionText));
                }
                back = phoneCallBackYQVO;
                return back;
            } else if (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0) {
                ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                //更新一下分数
                double score = 0.0;
                Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                if (ObjectUtils.isNotEmpty(scoreObj)) score = (double) scoreObj;
                serviceSubtask.setScore(String.valueOf(score));
                serviceSubtask.setFinishtime(new Date());
                serviceSubtask.setSendstate(6L);
                serviceSubtask.setRemark("电话拨打已完成");
                serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                //记录状态
                setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                //设置结束语
                phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                Long id = serviceSubtask.getId();
                Map<String, String> map = delRedisValue(null, id.toString());
                log.error("map的值为:{}", map);
                if (ObjectUtils.isNotEmpty(map)) redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                redisCache.deleteObject(phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid());
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "PlayEventCallbackPlaystop");
                //在redis中保存一下结束语,在调用挂电话的方法时删除
                ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                //结束语已拼进返回值,挂机由平台按返回值执行
                back = phoneCallBackYQVO;
                return back;
            }
        } else {
            //AI没有匹配上:由AI分支自己按"没问清就重问"的次数处理,不再退回正则识别
            log.info("ai没有匹配上----------------------------");
            //没匹配上也要把患者说的话记录下来(matchedtext置空,与正则路径口径一致)
            ServiceSubTaskDetailReq serviceSubTaskDetailReq = new ServiceSubTaskDetailReq();
            List<ServiceSubtaskDetail> serviceSubtaskDetailList = new ArrayList<>();
            ServiceSubtaskDetail serviceSubtaskDetail = getServiceSubtaskDetail(phoneCallReqYQVO, ivrTaskTemplateScriptVO, serviceSubtask, ivrTaskTemplate);
            //修改一下语音路径(改成前端可以访问的,存到数据库中)
            if (StringUtils.isNotEmpty(serviceSubtaskDetail.getQuestionvoice())) {
                String questionvoice = serviceSubtaskDetail.getQuestionvoice();
                String[] split = questionvoice.split("\\\\");
                if (split.length > 0) {
                    String lastPart = split[split.length - 1];
                    serviceSubtaskDetail.setQuestionvoice(voicePathPrefix + lastPart);
                }
            }
            serviceSubtaskDetailList.add(serviceSubtaskDetail);
            //如果没有 åŒ¹é…ä¸Šï¼Œè¿™ä¸ªå¿…须为null
            serviceSubtaskDetailList.get(0).setMatchedtext("");
            serviceSubTaskDetailReq.setServiceSubtaskDetailList(serviceSubtaskDetailList);
            serviceSubTaskDetailReq.setGuid(phoneCallReqYQVO.getGuid());
            serviceSubTaskDetailReq.setOrgid(phoneCallReqYQVO.getOrgid());
            saveQuestionAnswerPhone(serviceSubTaskDetailReq);
            String mateKey = phoneCallReqYQVO.getTaskid().trim() + "&&" + "mate" + "&&" + phoneCallReqYQVO.getUuid();
            Integer count = null;
            Object countObj = redisCache.getCacheObject(mateKey);
            if (ObjectUtils.isNotEmpty(countObj)) count = (Integer) countObj;
            if (count != null && count >= ivrTaskTemplate.getMateNum()) {
                //没匹配次数已经用满,不能再重问了
                boolean noNextQuestion = ("0".equals(ivrTaskTemplateScriptVO.getBranchFlag()) || "1".equals(ivrTaskTemplateScriptVO.getBranchFlag()))
                        && (ivrTaskTemplateScriptVO.getNextScriptno() == null || ivrTaskTemplateScriptVO.getNextScriptno() == 0);
                if (noNextQuestion) {
                    //已经是最后一题,直接挂机
                    ServiceTask serviceTask1 = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                    phoneCallBackYQVO.setType("text");
                    phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                    phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + serviceTask1.getJsy());
                    //更新一下分数
                    Double score = null;
                    Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                    serviceSubtask.setScore(String.valueOf(score));
                    serviceSubtask.setFinishtime(new Date());
                    serviceSubtask.setSendstate(6L);
                    serviceSubtask.setRemark("电话拨打已完成");
                    serviceSubtaskMapper.updateServiceSubtask(serviceSubtask);
                    //记录状态
                    setFailPreachForm(serviceSubtask, "3", "电话拨打已完成", "9");
                    //在redis中保存一下结束语,在调用挂电话的方法时删除
                    ServiceTask serviceTask = serviceTaskService.selectServiceTaskByTaskid(serviceSubtask.getTaskid());
                    redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "- jsy", serviceTask.getJsy(), 120, TimeUnit.MINUTES);
                    //去redis中,把该子任务ID删除
                    Long id = serviceSubtask.getId();
                    Map<String, String> map = delRedisValue(null, id.toString());
                    if (ObjectUtils.isNotEmpty(map)) {
                        redisCache.setCacheObject(map.get("cacheName"), map.get("val"));
                        redisCache.deleteObject(serviceSubtask.getId() + "-" + serviceSubtask.getPhone());
                        redisCache.deleteObject(phoneCallReqYQVO.getUuid() + "SCORE");
                    }
                } else {
                    //还有下一题,按nextScriptno继续提问
                    for (IvrTaskTemplateScript script : ivrTaskTemplateScripts) {
                        if (script.getSort() == ivrTaskTemplateScriptVO.getNextScriptno().intValue()) {
                            phoneCallBackYQVO.setType("text");
                            phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                            String scriptContent = StringUtils.isNotEmpty(script.getIvrtext()) ? script.getIvrtext() : script.getScriptContent();
                            phoneCallBackYQVO.setValue(phoneCallBackYQVO.getCommonValue() + getObject(serviceSubtask, scriptContent));
                            //将该患者的Redis中的题目ID,进行修改
                            redisCache.setCacheObject(phoneCallReqYQVO.getTaskid().trim() + "-" + phoneCallReqYQVO.getPhone().trim(), script.getId().toString(), 120, TimeUnit.MINUTES);
                            //更新一下分数
                            Double score = null;
                            Object scoreObj = redisCache.getCacheObject(phoneCallReqYQVO.getUuid() + "SCORE");
                            if (ObjectUtils.isNotEmpty(scoreObj)) score = (Double) scoreObj;
                            score = BigDecimal.valueOf(score).add(new BigDecimal(script.getScore())).doubleValue();
                            redisCache.setCacheObject(phoneCallReqYQVO.getUuid() + "SCORE", score, 120, TimeUnit.MINUTES);
                            //换题成功,没匹配次数清零
                            redisCache.deleteObject(mateKey);
                        }
                    }
                }
            } else {
                //还没问满次数,用noMatchText引导患者重答
                redisCache.setCacheObject(mateKey, count == null ? 1 : count + 1, 120, TimeUnit.MINUTES);
                phoneCallBackYQVO.setType("text");
                phoneCallBackYQVO.setSilent_interval(ivrTaskTemplate.getSilencetime().intValue());
                phoneCallBackYQVO.setValue(ivrTaskTemplateScriptVO.getNoMatchText() + getObject(serviceSubtask, StringUtils.isNotEmpty(ivrTaskTemplateScriptVO.getIvrtext()) ? ivrTaskTemplateScriptVO.getIvrtext() : ivrTaskTemplateScriptVO.getScriptContent()));
            }
            back = phoneCallBackYQVO;
            return back;
        }
        return back;
    }
    //获取任务组信息
    private void findServiceTaskScheduleVO(ServiceTaskVO serviceTaskVO) {
        if (StringUtils.isNotEmpty(serviceTaskVO.getAppltype()) && serviceTaskVO.getAppltype().equals("5")) {
@@ -5997,34 +4070,12 @@
        }
    }
    @Test
    public void TestMatch() {
        String questionText = "您好,请问您是患者本人还是家属?\n";
        String voiceText = "我自己";
        String matchedText = "";
        List<IvrTaskTemplateTargetoption> options = new ArrayList<>();
        IvrTaskTemplateTargetoption ivrTaskTemplateTargetoption1 = new IvrTaskTemplateTargetoption();
        ivrTaskTemplateTargetoption1.setTargetvalue("本人");
        options.add(ivrTaskTemplateTargetoption1);
        IvrTaskTemplateTargetoption ivrTaskTemplateTargetoption2 = new IvrTaskTemplateTargetoption();
        ivrTaskTemplateTargetoption2.setTargetvalue("家属");
        options.add(ivrTaskTemplateTargetoption2);
        IvrTaskTemplateTargetoption ivrTaskTemplateTargetoption3 = new IvrTaskTemplateTargetoption();
        ivrTaskTemplateTargetoption3.setTargetvalue("其他");
        options.add(ivrTaskTemplateTargetoption3);
        //1-AI识别
        String url = configService.selectConfigByKey("sys.voice.match.ai.url");
        Integer matched = matchOptionIndex(url, questionText, voiceText, options, "1");
        if (ObjectUtils.isNotEmpty(matched)) {
            if (matched >= 0) {
                matchedText = options.get(matched).getTargetvalue();
            }
        }
        log.info("ai匹配成功,匹配结果是:" + matchedText);
    }
    // ---------------------------------------------------------------------
    // è¯´æ˜Žï¼šåŽŸç§æœ‰ getWXCode / encode å·²éšå‘送方一起收口到 ruoyi-message æ¨¡å—
    // ï¼ˆXinHuaMpApi),此处已删除,避免三份拷贝各写一套签名逻辑。
    //
    // å¦ï¼šæœ¬ç±»åŽŸæœ¬è¿˜æœ‰ä¸€ä¸ª @Test æ ‡æ³¨çš„ TestMatch()(开发期手写的 AI é€‰é¡¹åŒ¹é…è°ƒè¯•方法),
    //     å…¨ä»“无任何调用点。它会把 org.junit æ‹–进【生产】源码依赖,而 ruoyi-task æ˜¯
    //     jar æ¨¡å—、不带 junit â€”— æ•…随模块化一并删除(属死代码,行为无影响)。
    // ---------------------------------------------------------------------
}
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskScheduleServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceTaskScheduleServiceImpl.java ÐÞ¸Ä
@@ -1,19 +1,9 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.ServiceSubtaskPreachform;
import com.smartor.domain.ServiceTaskSchedule;
import com.smartor.domain.ServiceTaskScheduleDetail;
import com.smartor.domain.ServiceTaskScheduleVO;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.smartor.mapper.ServiceSubtaskPreachformMapper;
import com.smartor.mapper.ServiceTaskScheduleDetailMapper;
import com.smartor.mapper.ServiceTaskScheduleMapper;
import com.smartor.service.IServiceTaskScheduleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -23,6 +13,16 @@
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.mapper.ServiceTaskScheduleDetailMapper;
import com.ruoyi.task.mapper.ServiceTaskScheduleMapper;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.api.spi.TaskPreachformPort;
import com.ruoyi.task.domain.ServiceTaskScheduleVO;
import com.ruoyi.task.service.IServiceTaskScheduleService;
/**
 * éšè®¿ä»»åŠ¡è®¡åˆ’ Service å®žçް
@@ -44,7 +44,7 @@
    private ServiceSubtaskMapper serviceSubtaskMapper;
    @Autowired
    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
    private TaskPreachformPort taskPreachformPort;
    /** å•次扫描的最大计划数,避免长事务。 */
    private static final int ROLL_BATCH_LIMIT = 200;
@@ -319,7 +319,7 @@
        ServiceSubtaskPreachform q = new ServiceSubtaskPreachform();
        q.setSubid(srcSubId);
        q.setTaskid(newSubtask.getTaskid());
        List<ServiceSubtaskPreachform> list = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(q);
        List<ServiceSubtaskPreachform> list = taskPreachformPort.selectServiceSubtaskPreachformList(q);
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
@@ -333,7 +333,7 @@
            pf.setSendstate("1");
            pf.setOrgid(newSubtask.getOrgid());
            pf.setCreateTime(new Date());
            serviceSubtaskPreachformMapper.insertServiceSubtaskPreachform(pf);
            taskPreachformPort.insertServiceSubtaskPreachform(pf);
        }
    }
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceTaskServiceImpl.java ÐÞ¸Ä
@@ -1,4 +1,4 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import java.util.*;
@@ -9,16 +9,12 @@
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.DtoConversionUtils;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import com.smartor.domain.*;
import com.smartor.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.smartor.service.IServiceTaskService;
import org.springframework.transaction.annotation.Transactional;
import com.ruoyi.template.domain.HeLibrary;
@@ -43,6 +39,18 @@
import com.ruoyi.template.mapper.SvyTaskTemplateScriptMapper;
import com.ruoyi.template.mapper.SvyTaskTemplateTargetoptionMapper;
import com.ruoyi.template.api.TemplateFacade;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.ServiceTaskdept;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
import com.ruoyi.task.mapper.ServiceTaskMapper;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.api.spi.TaskPreachformPort;
import com.ruoyi.task.api.spi.TaskArchivePort;
import com.ruoyi.task.service.IServiceTaskService;
/**
 * è¯­éŸ³ä»»åŠ¡Service业务层处理
 *
@@ -77,9 +85,7 @@
    @Autowired
    private ServiceTaskdeptMapper serviceTaskdeptMapper;
    @Autowired
    private PatArchiveImportMapper patArchiveImportMapper;
    @Autowired
    private ServiceSubtaskPreachformMapper serviceSubtaskPreachformMapper;
    private TaskPreachformPort taskPreachformPort;
    @Autowired
    private SvyTaskTemplateScriptMapper svyTaskTemplateScriptMapper;
    @Autowired
@@ -87,7 +93,7 @@
    @Autowired
    private SvyLibTemplateScriptMapper svyLibTemplateScriptMapper;
    @Autowired
    private PatArchiveMapper patArchiveMapper;
    private TaskArchivePort taskArchivePort;
    @Autowired
    private SvyLibTemplateTargetoptionMapper svyLibTemplateTargetoptionMapper;
    @Autowired
@@ -297,13 +303,13 @@
            serviceSubtaskPreachform.setPreachform(serviceSubtask.getCurrentPreachform());
            serviceSubtaskPreachform.setSubid(serviceSubtask.getId());
            serviceSubtaskPreachform.setTaskid(serviceTask.getTaskid());
            List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
            List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform);
            if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) {
                ServiceSubtaskPreachform ssp = serviceSubtaskPreachforms.get(0);
                //人工无-3已领取状态(service_subtask_preachform.sendstate = 3)
                if(StringUtils.isNotEmpty(ssp.getPreachform()) && !ssp.getPreachform().equals("1")){
                    ssp.setSendstate("3");
                    serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(ssp);
                    taskPreachformPort.updateServiceSubtaskPreachform(ssp);
                }
            }
        }
@@ -340,19 +346,19 @@
            patMedInhospVO.setSerialnum(zyserialnum);
            List<PatMedInhosp> patMedInhospList = inhospVisitFacade.selectPatMedInhospListByCondition(patMedInhospVO);
            if (CollectionUtils.isNotEmpty(patMedInhospList)) {
                patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedInhospList.get(0).getPatid());
                patArchive = taskArchivePort.findByPatid(patMedInhospList.get(0).getPatid());
            }
        } else if (StringUtils.isNotEmpty(mzserialnum)) {
            PatMedOuthospQueryReq patMedOuthosp = new PatMedOuthospQueryReq();
            patMedOuthosp.setSerialnum(mzserialnum);
            List<PatMedOuthosp> patMedOuthosps = outhospDataFacade.callSpQueryOuthosp(patMedOuthosp);
            if (CollectionUtils.isNotEmpty(patMedOuthosps)) {
                patArchive = patArchiveMapper.selectPatArchiveByPatid(patMedOuthosps.get(0).getPatid());
                patArchive = taskArchivePort.findByPatid(patMedOuthosps.get(0).getPatid());
            }
        } else if (StringUtils.isNotEmpty(tsserialnum)) {
            PatArchive pa = new PatArchive();
            pa.setPatientno(tsserialnum);
            List<PatArchive> patArchives = patArchiveMapper.selectPatArchiveList(pa);
            List<PatArchive> patArchives = taskArchivePort.findList(pa);
            if (CollectionUtils.isNotEmpty(patArchives)) patArchive = patArchives.get(0);
        }
@@ -431,10 +437,9 @@
        //获取患者信息
        PatArchive patArchive = null;
        if (patfrom.equals("0")) patArchive = patArchiveMapper.selectPatArchiveByPatid(patid);
        if (patfrom.equals("0")) patArchive = taskArchivePort.findByPatid(patid);
        else if (patfrom.equals("1")) {
            PatArchiveImport patArchiveImport = patArchiveImportMapper.selectPatArchiveImportById(patid);
            patArchive = DtoConversionUtils.sourceToTarget(patArchiveImport, PatArchive.class);
            patArchive = taskArchivePort.findImportedByPatid(patid);
        }
        //通过患者信息和任务信息中的textParam对问题中的变量进行填充
@@ -523,10 +528,9 @@
        }
        //获取患者信息
        PatArchive patArchive = null;
        if (patfrom.equals("0")) patArchive = patArchiveMapper.selectPatArchiveByPatid(patid);
        if (patfrom.equals("0")) patArchive = taskArchivePort.findByPatid(patid);
        else if (patfrom.equals("1")) {
            PatArchiveImport patArchiveImport = patArchiveImportMapper.selectPatArchiveImportById(patid);
            patArchive = DtoConversionUtils.sourceToTarget(patArchiveImport, PatArchive.class);
            patArchive = taskArchivePort.findImportedByPatid(patid);
        }
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdeptServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceTaskdeptServiceImpl.java ÐÞ¸Ä
@@ -1,14 +1,14 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.ServiceTaskdept;
import com.smartor.mapper.ServiceTaskdeptMapper;
import com.smartor.service.IServiceTaskdeptService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskdept;
import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
import com.ruoyi.task.service.IServiceTaskdeptService;
/**
 * æ¨¡æ¿ç§‘室关联Service业务层处理
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskdiagServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceTaskdiagServiceImpl.java ÐÞ¸Ä
@@ -1,12 +1,12 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.smartor.mapper.ServiceTaskdiagMapper;
import com.smartor.domain.ServiceTaskdiag;
import com.smartor.service.IServiceTaskdiagService;
import com.ruoyi.task.domain.ServiceTaskdiag;
import com.ruoyi.task.mapper.ServiceTaskdiagMapper;
import com.ruoyi.task.service.IServiceTaskdiagService;
/**
 * æ¨¡æ¿ç§‘室关联Service业务层处理
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskmedicalServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceTaskmedicalServiceImpl.java ÐÞ¸Ä
@@ -1,15 +1,15 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.smartor.domain.ServiceTaskmedical;
import com.smartor.domain.VO.ServiceTaskmedicalVO;
import com.smartor.mapper.ServiceTaskmedicalMapper;
import com.smartor.service.IServiceTaskmedicalService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import com.ruoyi.task.domain.ServiceTaskmedical;
import com.ruoyi.task.domain.VO.ServiceTaskmedicalVO;
import com.ruoyi.task.mapper.ServiceTaskmedicalMapper;
import com.ruoyi.task.service.IServiceTaskmedicalService;
/**
 * ä»»åŠ¡è¯å“å…³è”Service业务层处理
ruoyi-task/src/main/java/com/ruoyi/task/service/impl/ServiceTaskoperServiceImpl.java
ÎļþÃû´Ó smartor/src/main/java/com/smartor/service/impl/ServiceTaskoperServiceImpl.java ÐÞ¸Ä
@@ -1,12 +1,12 @@
package com.smartor.service.impl;
package com.ruoyi.task.service.impl;
import java.util.List;
import com.ruoyi.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.smartor.mapper.ServiceTaskoperMapper;
import com.smartor.domain.ServiceTaskoper;
import com.smartor.service.IServiceTaskoperService;
import com.ruoyi.task.domain.ServiceTaskoper;
import com.ruoyi.task.mapper.ServiceTaskoperMapper;
import com.ruoyi.task.service.IServiceTaskoperService;
/**
 * æ¨¡æ¿ç§‘室关联Service业务层处理
ruoyi-task/src/main/resources/mapper/task/IvrTaskVisitResultMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/IvrTaskVisitResultMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.IvrTaskVisitResultMapper">
<mapper namespace="com.ruoyi.task.mapper.IvrTaskVisitResultMapper">
    <resultMap type="com.smartor.domain.IvrTaskVisitResult" id="IvrTaskVisitResultResult">
    <resultMap type="com.ruoyi.task.domain.IvrTaskVisitResult" id="IvrTaskVisitResultResult">
        <result property="id" column="id"/>
        <result property="sendname" column="sendname"/>
        <result property="phone" column="phone"/>
@@ -57,7 +57,7 @@
        from ivr_task_visit_result
    </sql>
    <select id="selectIvrTaskVisitResultList" parameterType="com.smartor.domain.IvrTaskVisitResult"
    <select id="selectIvrTaskVisitResultList" parameterType="com.ruoyi.task.domain.IvrTaskVisitResult"
            resultMap="IvrTaskVisitResultResult">
        <include refid="selectIvrTaskVisitResultVo"/>
        where 1=1
@@ -86,7 +86,7 @@
        where id = #{id}
    </select>
    <insert id="insertIvrTaskVisitResult" parameterType="com.smartor.domain.IvrTaskVisitResult" useGeneratedKeys="true"
    <insert id="insertIvrTaskVisitResult" parameterType="com.ruoyi.task.domain.IvrTaskVisitResult" useGeneratedKeys="true"
            keyProperty="id">
        insert into ivr_task_visit_result
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -139,7 +139,7 @@
        </trim>
    </insert>
    <update id="updateIvrTaskVisitResult" parameterType="com.smartor.domain.IvrTaskVisitResult">
    <update id="updateIvrTaskVisitResult" parameterType="com.ruoyi.task.domain.IvrTaskVisitResult">
        update ivr_task_visit_result
        <trim prefix="SET" suffixOverrides=",">
            <if test="sendname != null">sendname = #{sendname},</if>
ruoyi-task/src/main/resources/mapper/task/ServiceOutPathMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceOutPathMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceOutPathMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceOutPathMapper">
    <resultMap type="com.smartor.domain.ServiceOutPath" id="ServiceOutPathResult">
    <resultMap type="com.ruoyi.task.domain.ServiceOutPath" id="ServiceOutPathResult">
        <result property="id" column="id"/>
        <result property="param1" column="param1"/>
        <result property="param2" column="param2"/>
@@ -50,7 +50,7 @@
        from service_out_path
    </sql>
    <select id="selectServiceOutPathList" parameterType="com.smartor.domain.ServiceOutPath"
    <select id="selectServiceOutPathList" parameterType="com.ruoyi.task.domain.ServiceOutPath"
            resultMap="ServiceOutPathResult">
        <include refid="selectServiceOutPathVo"/>
        where 1=1
@@ -106,7 +106,7 @@
        where id = #{id}
    </select>
    <insert id="insertServiceOutPath" parameterType="com.smartor.domain.ServiceOutPath" useGeneratedKeys="true"
    <insert id="insertServiceOutPath" parameterType="com.ruoyi.task.domain.ServiceOutPath" useGeneratedKeys="true"
            keyProperty="id">
        insert into service_out_path
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -179,7 +179,7 @@
        </trim>
    </insert>
    <update id="updateServiceOutPath" parameterType="com.smartor.domain.ServiceOutPath">
    <update id="updateServiceOutPath" parameterType="com.ruoyi.task.domain.ServiceOutPath">
        update service_out_path
        <trim prefix="SET" suffixOverrides=",">
            <if test="param1 != null">param1 =
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskDetailMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceSubtaskDetailMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceSubtaskDetailMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceSubtaskDetailMapper">
    <resultMap type="com.smartor.domain.ServiceSubtaskDetail" id="ServiceSubtaskDetailResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskDetail" id="ServiceSubtaskDetailResult">
        <result property="id" column="id"/>
        <result property="subId" column="sub_id"/>
        <result property="targetid" column="targetid"/>
@@ -62,7 +62,7 @@
        <result property="libTemplateid" column="lib_templateid"/>
        <result property="score" column="score"/>
    </resultMap>
    <resultMap type="com.smartor.domain.ServiceSubtaskDetailTarget" id="ServiceSubtaskDetailTargetResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskDetailTarget" id="ServiceSubtaskDetailTargetResult">
        <result property="targetid" column="targetid"/>
        <result property="taskid" column="taskid"/>
        <result property="targetname" column="targetname"/>
@@ -128,7 +128,7 @@
        from service_subtask_detail
    </sql>
    <select id="selectServiceSubtaskDetailList" parameterType="com.smartor.domain.ServiceSubtaskDetail"
    <select id="selectServiceSubtaskDetailList" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetail"
            resultMap="ServiceSubtaskDetailResult">
        <include refid="selectServiceSubtaskDetailVo"/>
        where 1=1
@@ -203,7 +203,7 @@
        group by scriptid
    </select>
    <select id="getServiceSubtaskDetails" parameterType="com.smartor.domain.ServiceSubtaskDetailVO"
    <select id="getServiceSubtaskDetails" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetailVO"
            resultType="java.util.HashMap">
        select
            ssd.sub_id,
@@ -272,7 +272,7 @@
        where id = #{id}
    </select>
    <select id="queryFillCount" resultType="com.smartor.domain.DTO.ServiceSubtaskDetailDTO">
    <select id="queryFillCount" resultType="com.ruoyi.task.domain.DTO.ServiceSubtaskDetailDTO">
        select id,questiontext,matchedtext,targetvalue,score
        from service_subtask_detail
        where del_flag = 0
@@ -283,7 +283,7 @@
        <if test="questiontext != null">and questiontext = #{questiontext}</if>
    </select>
                <select id="isExistServiceSubtaskDetail" resultType="com.smartor.domain.DTO.ServiceSubtaskDetailDTO">
                <select id="isExistServiceSubtaskDetail" resultType="com.ruoyi.task.domain.DTO.ServiceSubtaskDetailDTO">
        select id, questiontext, matchedtext, score
        from service_subtask_detail
        where del_flag = 0
@@ -291,7 +291,7 @@
        <if test="scriptid != null">and scriptid = #{scriptid}</if>
    </select>
    <insert id="insertServiceSubtaskDetail" parameterType="com.smartor.domain.ServiceSubtaskDetail"
    <insert id="insertServiceSubtaskDetail" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetail"
            useGeneratedKeys="true"
            keyProperty="id">
        insert into service_subtask_detail
@@ -411,7 +411,7 @@
        </trim>
    </insert>
    <update id="updateServiceSubtaskDetail" parameterType="com.smartor.domain.ServiceSubtaskDetail">
    <update id="updateServiceSubtaskDetail" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetail">
        update service_subtask_detail
        <trim prefix="SET" suffixOverrides=",">
            <if test="isMydException != null">is_myd_exception = #{isMydException},</if>
@@ -472,7 +472,7 @@
        where id = #{id}
    </update>
    <update id="updateSSDByCondition" parameterType="com.smartor.domain.ServiceSubtaskDetail">
    <update id="updateSSDByCondition" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetail">
        update service_subtask_detail
        <trim prefix="SET" suffixOverrides=",">
            <if test="subId != null">sub_id = #{subId},</if>
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskDetailTraceMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceSubtaskDetailTraceMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceSubtaskDetailTraceMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceSubtaskDetailTraceMapper">
    <resultMap type="com.smartor.domain.ServiceSubtaskDetailTrace" id="ServiceSubtaskDetailTtraceResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskDetailTrace" id="ServiceSubtaskDetailTtraceResult">
        <result property="id" column="id"/>
        <result property="detailId" column="detail_id"/>
        <result property="subId" column="sub_id"/>
@@ -139,7 +139,7 @@
        from service_subtask_detail_trace
    </sql>
    <select id="selectServiceSubtaskDetailTtraceList" parameterType="com.smartor.domain.VO.ServiceSubtaskDetailTraceVO"
    <select id="selectServiceSubtaskDetailTtraceList" parameterType="com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO"
            resultMap="ServiceSubtaskDetailTtraceResult">
        <include refid="selectServiceSubtaskDetailTtraceVo"/>
        where del_flag=0
@@ -492,7 +492,7 @@
        where del_flag=0 and id = #{id}
    </select>
    <insert id="insertServiceSubtaskDetailTtrace" parameterType="com.smartor.domain.ServiceSubtaskDetailTrace"
    <insert id="insertServiceSubtaskDetailTtrace" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetailTrace"
            useGeneratedKeys="true" keyProperty="id">
        insert into service_subtask_detail_trace
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -767,7 +767,7 @@
        </trim>
    </insert>
    <update id="updateServiceSubtaskDetailTtrace" parameterType="com.smartor.domain.ServiceSubtaskDetailTrace">
    <update id="updateServiceSubtaskDetailTtrace" parameterType="com.ruoyi.task.domain.ServiceSubtaskDetailTrace">
        update service_subtask_detail_trace
        <trim prefix="SET" suffixOverrides=",">
            <if test="detailId != null">detail_id =
ruoyi-task/src/main/resources/mapper/task/ServiceSubtaskMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceSubtaskMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceSubtaskMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceSubtaskMapper">
    <resultMap type="com.smartor.domain.ServiceSubtask" id="ServiceSubtaskResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtask" id="ServiceSubtaskResult">
        <result property="id" column="id"/>
        <result property="sendname" column="sendname"/>
        <result property="phone" column="phone"/>
@@ -102,7 +102,7 @@
        <result property="campusid" column="campusid"/>
    </resultMap>
    <resultMap type="com.smartor.domain.ServiceSubtaskCount" id="ServiceSubtaskResult2">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskCount" id="ServiceSubtaskResult2">
        <result property="month" column="month"/>
        <result property="serviceType" column="service_type"/>
        <result property="subTaskId" column="subTaskId"/>
@@ -113,7 +113,7 @@
    </resultMap>
    <resultMap type="com.smartor.domain.ServiceSubtaskScriptCount" id="ServiceSubtaskScriptCountResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskScriptCount" id="ServiceSubtaskScriptCountResult">
        <result property="scriptid" column="scriptid"/>
        <result property="templateid" column="templateid"/>
        <result property="scriptType" column="script_type"/>
@@ -128,7 +128,7 @@
        <result property="options" column="options"/>
    </resultMap>
    <resultMap type="com.smartor.domain.ServiceSubtaskStatistic" id="ServiceSubtaskStatisticResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskStatistic" id="ServiceSubtaskStatisticResult">
        <result property="taskName" column="task_name"/>
        <result property="drcode" column="drcode"/>
        <result property="drname" column="drname"/>
@@ -251,7 +251,7 @@
        from service_subtask
    </sql>
    <select id="selectServiceSubtaskBySendstate" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
    <select id="selectServiceSubtaskBySendstate" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
@@ -274,7 +274,7 @@
        </if>
    </select>
    <select id="queryServiceSubtaskList" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
    <select id="queryServiceSubtaskList" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
@@ -291,7 +291,7 @@
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
    </select>
    <select id="selectServiceSubtaskList" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
    <select id="selectServiceSubtaskList" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where del_flag = 0
@@ -536,7 +536,7 @@
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
    </select>
            <select id="getCompensateServiceSubtaskList" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
            <select id="getCompensateServiceSubtaskList" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where del_flag = 0
@@ -569,7 +569,7 @@
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
    </select>
    <select id="selectServiceSubtaskListByCreateTime" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
    <select id="selectServiceSubtaskListByCreateTime" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where del_flag = 0
@@ -598,7 +598,7 @@
    </select>
    <select id="selectServiceSubtaskListAgain" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
    <select id="selectServiceSubtaskListAgain" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
@@ -658,7 +658,7 @@
        <if test="continueTimeNext != null ">and continue_time_next = #{continueTimeNext}</if>
    </select>
    <select id="selectSendstateByCondition" parameterType="com.smartor.domain.ServiceSubtask" resultType="Integer">
    <select id="selectSendstateByCondition" parameterType="com.ruoyi.task.domain.ServiceSubtask" resultType="Integer">
        SELECT b.sendstate
        from service_task a
        JOIN service_subtask b on a.taskid = b.taskid and a.pat_cycle = 0 and b.del_flag = 0
@@ -673,7 +673,7 @@
        <if test="campusid != null and campusid != ''">and b.campusid = #{campusid}</if>
    </select>
    <select id="selectTimelyRate" parameterType="com.smartor.domain.ServiceSubtask" resultType="double">
    <select id="selectTimelyRate" parameterType="com.ruoyi.task.domain.ServiceSubtask" resultType="double">
        SELECT sum( a.finished )/ sum( 1 ) FROM
        (SELECT id, visit_time, finishtime,
        (CASE WHEN ( DATE_FORMAT( visit_time, '%y%m%d' )&lt; DATE_FORMAT( IF ( ISNULL( finishtime ), NOW(), finishtime
@@ -711,7 +711,7 @@
        ) a
    </select>
    <select id="selectTimelyRateBatch" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity" resultType="map">
    <select id="selectTimelyRateBatch" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity" resultType="map">
        SELECT
        <if test="groupKey != null and groupKey != ''">
            ss.${groupKey} as groupKey,
@@ -790,7 +790,7 @@
        </if>
    </select>
    <select id="selectTimelyRateDetail" parameterType="com.smartor.domain.ServiceSubtask"
    <select id="selectTimelyRateDetail" parameterType="com.ruoyi.task.domain.ServiceSubtask"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
@@ -830,7 +830,7 @@
        where id = #{id}
    </select>
    <select id="selectServiceSubtaskByDiagname" parameterType="com.smartor.domain.ServiceSubtask"
    <select id="selectServiceSubtaskByDiagname" parameterType="com.ruoyi.task.domain.ServiceSubtask"
            resultMap="ServiceSubtaskResult">
        select sendstate,starttime,endtime,nurse_name,phone,sex,task_name,sendname,leavediagname,visit_time FROM
        service_subtask WHERE del_flag=0
@@ -856,7 +856,7 @@
        <if test="pageSize != null  and pageNum != null">limit ${pageSize} OFFSET ${pageNum}</if>
    </select>
    <insert id="insertServiceSubtask" parameterType="com.smartor.domain.ServiceSubtask" useGeneratedKeys="true"
    <insert id="insertServiceSubtask" parameterType="com.ruoyi.task.domain.ServiceSubtask" useGeneratedKeys="true"
            keyProperty="id">
        insert into service_subtask
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -1057,7 +1057,7 @@
        </trim>
    </insert>
    <update id="updateServiceSubtask" parameterType="com.smartor.domain.ServiceSubtask">
    <update id="updateServiceSubtask" parameterType="com.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
            <if test="sendname != null">sendname = #{sendname},</if>
@@ -1161,7 +1161,7 @@
        where id = #{id}
    </update>
    <update id="updateServiceSubtaskByPatId" parameterType="com.smartor.domain.ServiceSubtask">
    <update id="updateServiceSubtaskByPatId" parameterType="com.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
            <if test="sendname != null">sendname = #{sendname},</if>
@@ -1265,7 +1265,7 @@
        <if test="campusid != null and campusid != ''">and campusid = #{campusid}</if>
    </update>
    <update id="updateServiceSubtaskByCondition" parameterType="com.smartor.domain.ServiceSubtask">
    <update id="updateServiceSubtaskByCondition" parameterType="com.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
            <if test="sendname != null">sendname = #{sendname},</if>
@@ -1377,7 +1377,7 @@
        </where>
    </update>
    <update id="updateServiceSubtaskByTaskGuid" parameterType="com.smartor.domain.ServiceSubtask">
    <update id="updateServiceSubtaskByTaskGuid" parameterType="com.ruoyi.task.domain.ServiceSubtask">
        update service_subtask
        <trim prefix="SET" suffixOverrides=",">
            <if test="sendname != null">sendname = #{sendname},</if>
@@ -1491,7 +1491,7 @@
        where patid = #{patid} and taskid = #{taskid}
    </update>
    <select id="getSfStatistics" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatistics" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult">
        select id,
        visit_count,
@@ -1597,7 +1597,7 @@
        </if>
    </select>
    <select id="getSpecialSfStatistics" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSpecialSfStatistics" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskStatisticResult">
        select
        <if test="groupKey != null and groupKey == 'task_name'">
@@ -1710,7 +1710,7 @@
        </if>
    </select>
    <select id="getSfStatisticsGroupKey" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsGroupKey" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultType="String">
        select
        <if test="groupKey != null and groupKey != ''">
@@ -1779,7 +1779,7 @@
        </if>
    </select>
    <select id="getSfStatisticsHyperlink" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsHyperlink" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult">
        select id,
        visit_count,
@@ -1912,7 +1912,7 @@
        </if>
    </select>
    <select id="getSfStatisticsJoy" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsJoy" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult2">
        SELECT sub_id as subTaskId,COUNT(sub_id) joyCount,
        (SELECT COUNT(1) FROM ivr_liba_target WHERE assortid IN (SELECT config_value FROM sys_config WHERE
@@ -1961,7 +1961,7 @@
        GROUP BY sub_id
    </select>
    <select id="getSfStatisticsJoydetails" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsJoydetails" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult2">
        SELECT joyName,joyCount,joyAllCount,ROUND(joyCount/joyAllCount,2) joyTotal FROM (
        SELECT targetname joyName,
@@ -2042,7 +2042,7 @@
        )a1
    </select>
    <select id="getSfStatisticsCategory" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsCategory" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult2">
        SELECT
        <if test="configValue != null and configValue != ''">
@@ -2115,7 +2115,7 @@
        GROUP BY a.sub_id
    </select>
    <select id="getSfStatisticsScriptDetails" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsScriptDetails" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskScriptCountResult">
        SELECT
        a.id as scriptid,
@@ -2196,7 +2196,7 @@
        </if>
    </select>
    <select id="getSfStatisticsScriptItemDetails" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfStatisticsScriptItemDetails" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskScriptCountResult">
        SELECT
        ssd.scriptid,
@@ -2338,7 +2338,7 @@
    </select>
    <!--    èŽ·å–éšè®¿é—¨è¯Šæ¯æœˆçš„æ•°é‡-->
    <select id="getSfFzInfoEveryMonth" parameterType="com.smartor.domain.ServiceSubtaskCountReq"
    <select id="getSfFzInfoEveryMonth" parameterType="com.ruoyi.task.domain.ServiceSubtaskCountReq"
            resultMap="ServiceSubtaskResult2">
        SELECT DATE_FORMAT(st.finishtime, '%Y-%m') AS month,
              st.service_type as service_type,
@@ -2380,8 +2380,8 @@
    </select>
    <!-- æœåŠ¡ç»Ÿè®¡æŸ¥è¯¢ - æŒ‰æ—¶é—´ç»´åº¦ç»Ÿè®¡å‡ºé™¢/门诊随访数据 -->
    <select id="getServiceStatistics" parameterType="com.smartor.domain.ServiceStatisticsRequest"
            resultType="com.smartor.domain.ServiceStatisticsResponse">
    <select id="getServiceStatistics" parameterType="com.ruoyi.task.domain.ServiceStatisticsRequest"
            resultType="com.ruoyi.task.domain.ServiceStatisticsResponse">
        SELECT
        timePeriod,
        SUM(dischargeFollowCount) AS dischargeFollowCount,
@@ -2549,7 +2549,7 @@
    </select>
    <!-- ä¼˜åŒ–的统计查询:直接在数据库层完成聚合计算 -->
    <select id="countByCondition" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity" resultType="map">
    <select id="countByCondition" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity" resultType="map">
        SELECT
        SUM(CASE WHEN sendstate = 4 THEN 1 ELSE 0 END) AS wzx,
        SUM(CASE WHEN sendstate != 4 AND sendstate != 2 THEN 1 ELSE 0 END) AS ysf,
@@ -2786,7 +2786,7 @@
        </if>
    </select>
    <select id="getCurrentUserServiceSubtaskCount" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
    <select id="getCurrentUserServiceSubtaskCount" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultType="map">
        SELECT
        COUNT(CASE WHEN sendstate = 2 AND date_format(visit_time,'%y%m%d') &lt;= date_format(CURDATE(),'%y%m%d') THEN 1
@@ -2830,7 +2830,7 @@
    -->
    <!-- æŒ‰ç—…区分组统计延续护理数量 -->
    <select id="getContinueNurseCount"
            parameterType="com.smartor.domain.VO.ServiceSubtaskCotinueCountVO"
            parameterType="com.ruoyi.task.domain.VO.ServiceSubtaskCotinueCountVO"
            resultType="java.util.Map">
        SELECT
        leavehospitaldistrictcode AS groupCode,
@@ -2859,7 +2859,7 @@
    <!-- æŒ‰ç§‘室分组统计延续护理数量 -->
    <select id="getContinueNurseCountByDept"
            parameterType="com.smartor.domain.VO.ServiceSubtaskCotinueCountVO"
            parameterType="com.ruoyi.task.domain.VO.ServiceSubtaskCotinueCountVO"
            resultType="java.util.Map">
        SELECT
        deptcode AS groupCode,
@@ -3045,7 +3045,7 @@
        </if>
    </select>
                    <select id="selectServiceSubtaskListDeadLine" parameterType="com.smartor.domain.entity.ServiceSubtaskEntity"
                    <select id="selectServiceSubtaskListDeadLine" parameterType="com.ruoyi.task.domain.entity.ServiceSubtaskEntity"
            resultMap="ServiceSubtaskResult">
        <include refid="selectServiceSubtaskVo"/>
        where 1=1
ruoyi-task/src/main/resources/mapper/task/ServiceTaskMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskMapper">
    <resultMap type="com.smartor.domain.ServiceTask" id="ServiceTaskResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTask" id="ServiceTaskResult">
        <result property="taskid" column="taskid"/>
        <result property="taskName" column="task_name"/>
        <result property="templateid" column="templateid"/>
@@ -124,7 +124,7 @@
        from service_task
    </sql>
    <select id="selectServiceTaskList" parameterType="com.smartor.domain.ServiceTask" resultMap="ServiceTaskResult">
    <select id="selectServiceTaskList" parameterType="com.ruoyi.task.domain.ServiceTask" resultMap="ServiceTaskResult">
        <include refid="selectServiceTaskVo"/>
        where 1=1
        and del_flag = 0
@@ -200,7 +200,7 @@
        order by update_time desc,taskid desc
    </select>
    <select id="selectServiceTaskAndtaskDeptList" parameterType="com.smartor.domain.ServiceTask"
    <select id="selectServiceTaskAndtaskDeptList" parameterType="com.ruoyi.task.domain.ServiceTask"
            resultMap="ServiceTaskResult">
        select service_task.taskid,
        service_task.task_name,
@@ -432,7 +432,7 @@
        where taskid = #{taskid} and del_flag=0
    </select>
    <insert id="insertServiceTask" parameterType="com.smartor.domain.ServiceTask" useGeneratedKeys="true"
    <insert id="insertServiceTask" parameterType="com.ruoyi.task.domain.ServiceTask" useGeneratedKeys="true"
            keyProperty="taskid">
        insert into service_task
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -551,7 +551,7 @@
        </trim>
    </insert>
    <update id="updateServiceTask" parameterType="com.smartor.domain.ServiceTask">
    <update id="updateServiceTask" parameterType="com.ruoyi.task.domain.ServiceTask">
        update service_task
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskName != null and taskName != ''">task_name = #{taskName},</if>
ruoyi-task/src/main/resources/mapper/task/ServiceTaskRuleMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskRuleMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskRuleMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskRuleMapper">
    <resultMap type="com.smartor.domain.ServiceTaskRule" id="ServiceTaskRuleResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskRule" id="ServiceTaskRuleResult">
            <result property="id" column="id"/>
            <result property="deptRule" column="dept_rule"/>
            <result property="wradRule" column="wrad_rule"/>
@@ -31,7 +31,7 @@
        from service_task_rule
    </sql>
    <select id="selectServiceTaskRuleList" parameterType="com.smartor.domain.ServiceTaskRule"
    <select id="selectServiceTaskRuleList" parameterType="com.ruoyi.task.domain.ServiceTaskRule"
            resultMap="ServiceTaskRuleResult">
        <include refid="selectServiceTaskRuleVo"/>
        where 1=1
@@ -83,7 +83,7 @@
            where id = #{id}
    </select>
    <select id="selectServiceTaskRuleByDeptOrWard" parameterType="com.smartor.domain.ServiceTaskRule"
    <select id="selectServiceTaskRuleByDeptOrWard" parameterType="com.ruoyi.task.domain.ServiceTaskRule"
            resultMap="ServiceTaskRuleResult">
            <include refid="selectServiceTaskRuleVo"/>
            where del_flag = 0
@@ -97,7 +97,7 @@
            limit 1
    </select>
    <insert id="insertServiceTaskRule" parameterType="com.smartor.domain.ServiceTaskRule"            useGeneratedKeys="true" keyProperty="id">
    <insert id="insertServiceTaskRule" parameterType="com.ruoyi.task.domain.ServiceTaskRule"            useGeneratedKeys="true" keyProperty="id">
        insert into service_task_rule
        <trim prefix="(" suffix=")" suffixOverrides=",">
                    <if test="deptRule != null">dept_rule,
@@ -177,7 +177,7 @@
        </trim>
    </insert>
    <update id="updateServiceTaskRule" parameterType="com.smartor.domain.ServiceTaskRule">
    <update id="updateServiceTaskRule" parameterType="com.ruoyi.task.domain.ServiceTaskRule">
        update service_task_rule
        <trim prefix="SET" suffixOverrides=",">
                    <if test="deptRule != null">dept_rule =
ruoyi-task/src/main/resources/mapper/task/ServiceTaskScheduleDetailMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleDetailMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskScheduleDetailMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskScheduleDetailMapper">
    <resultMap type="com.smartor.domain.ServiceTaskScheduleDetail" id="ServiceTaskScheduleDetailResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskScheduleDetail" id="ServiceTaskScheduleDetailResult">
        <id property="id" column="id"/>
        <result property="taskid" column="taskid"/>
        <result property="scheduleid" column="scheduleid"/>
@@ -54,7 +54,7 @@
    </select>
    <select id="selectServiceTaskScheduleDetailList"
            parameterType="com.smartor.domain.ServiceTaskScheduleDetail"
            parameterType="com.ruoyi.task.domain.ServiceTaskScheduleDetail"
            resultMap="ServiceTaskScheduleDetailResult">
        <include refid="selectServiceTaskScheduleDetailVo"/>
        where del_flag = '0'
@@ -77,7 +77,7 @@
        order by seq asc
    </select>
    <insert id="insertServiceTaskScheduleDetail" parameterType="com.smartor.domain.ServiceTaskScheduleDetail"
    <insert id="insertServiceTaskScheduleDetail" parameterType="com.ruoyi.task.domain.ServiceTaskScheduleDetail"
            useGeneratedKeys="true" keyProperty="id">
        insert into service_task_schedule_detail
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -138,7 +138,7 @@
        </foreach>
    </insert>
    <update id="updateServiceTaskScheduleDetail" parameterType="com.smartor.domain.ServiceTaskScheduleDetail">
    <update id="updateServiceTaskScheduleDetail" parameterType="com.ruoyi.task.domain.ServiceTaskScheduleDetail">
        update service_task_schedule_detail
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskid != null">taskid = #{taskid},</if>
ruoyi-task/src/main/resources/mapper/task/ServiceTaskScheduleMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskScheduleMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskScheduleMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskScheduleMapper">
    <resultMap type="com.smartor.domain.ServiceTaskSchedule" id="ServiceTaskScheduleResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskSchedule" id="ServiceTaskScheduleResult">
        <id property="id" column="id"/>
        <result property="taskid" column="taskid"/>
        <result property="maxLoopCount" column="max_loop_count"/>
@@ -71,7 +71,7 @@
        limit 1
    </select>
    <select id="selectServiceTaskScheduleList" parameterType="com.smartor.domain.ServiceTaskSchedule"
    <select id="selectServiceTaskScheduleList" parameterType="com.ruoyi.task.domain.ServiceTaskSchedule"
            resultMap="ServiceTaskScheduleResult">
        <include refid="selectServiceTaskScheduleVo"/>
        where del_flag = '0'
@@ -86,7 +86,7 @@
        order by id desc
    </select>
    <insert id="insertServiceTaskSchedule" parameterType="com.smartor.domain.ServiceTaskSchedule"
    <insert id="insertServiceTaskSchedule" parameterType="com.ruoyi.task.domain.ServiceTaskSchedule"
            useGeneratedKeys="true" keyProperty="id">
        insert into service_task_schedule
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -141,7 +141,7 @@
        </trim>
    </insert>
    <update id="updateServiceTaskSchedule" parameterType="com.smartor.domain.ServiceTaskSchedule">
    <update id="updateServiceTaskSchedule" parameterType="com.ruoyi.task.domain.ServiceTaskSchedule">
        update service_task_schedule
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskid != null">taskid = #{taskid},</if>
ruoyi-task/src/main/resources/mapper/task/ServiceTaskdeptMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskdeptMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskdeptMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskdeptMapper">
    <resultMap type="com.smartor.domain.ServiceTaskdept" id="ServiceTaskdeptResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskdept" id="ServiceTaskdeptResult">
        <result property="id" column="id"/>
        <result property="taskId" column="task_id"/>
        <result property="taskName" column="task_name"/>
@@ -37,7 +37,7 @@
        from service_taskdept
    </sql>
    <select id="selectServiceTaskdeptList" parameterType="com.smartor.domain.ServiceTaskdept"
    <select id="selectServiceTaskdeptList" parameterType="com.ruoyi.task.domain.ServiceTaskdept"
            resultMap="ServiceTaskdeptResult">
        <include refid="selectServiceTaskdeptVo"/>
        where 1=1
@@ -77,7 +77,7 @@
        where del_flag=0 and id = #{id}
    </select>
    <insert id="insertServiceTaskdept" parameterType="com.smartor.domain.ServiceTaskdept" useGeneratedKeys="true"
    <insert id="insertServiceTaskdept" parameterType="com.ruoyi.task.domain.ServiceTaskdept" useGeneratedKeys="true"
            keyProperty="id">
        insert into service_taskdept
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -134,7 +134,7 @@
        </trim>
    </insert>
    <update id="updateServiceTaskdept" parameterType="com.smartor.domain.ServiceTaskdept">
    <update id="updateServiceTaskdept" parameterType="com.ruoyi.task.domain.ServiceTaskdept">
        update service_taskdept
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskId != null">task_id =
@@ -187,7 +187,7 @@
        </trim>
        where id = #{id}
    </update>
    <update id="deleteServiceTaskdeptByTaskId" parameterType="com.smartor.domain.ServiceTaskdept">
    <update id="deleteServiceTaskdeptByTaskId" parameterType="com.ruoyi.task.domain.ServiceTaskdept">
        update service_taskdept
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1,
ruoyi-task/src/main/resources/mapper/task/ServiceTaskdiagMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskdiagMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskdiagMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskdiagMapper">
    <resultMap type="com.smartor.domain.ServiceTaskdiag" id="ServiceTaskdiagResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskdiag" id="ServiceTaskdiagResult">
        <result property="id" column="id"/>
        <result property="taskId" column="task_id"/>
        <result property="longtask" column="longtask"/>
@@ -49,7 +49,7 @@
        from service_taskdiag
    </sql>
    <select id="selectServiceTaskdiagList" parameterType="com.smartor.domain.ServiceTaskdiag"
    <select id="selectServiceTaskdiagList" parameterType="com.ruoyi.task.domain.ServiceTaskdiag"
            resultMap="ServiceTaskdiagResult">
        <include refid="selectServiceTaskdiagVo"/>
        where 1=1
@@ -101,7 +101,7 @@
            </if>
    </select>
    <select id="selectServiceTaskdiagListByIcdName" parameterType="com.smartor.domain.ServiceTaskdiag"
    <select id="selectServiceTaskdiagListByIcdName" parameterType="com.ruoyi.task.domain.ServiceTaskdiag"
            resultMap="ServiceTaskdiagResult">
        <include refid="selectServiceTaskdiagVo"/>
        where 1=1
@@ -159,7 +159,7 @@
        where id = #{id}
    </select>
    <insert id="insertServiceTaskdiag" parameterType="com.smartor.domain.ServiceTaskdiag" useGeneratedKeys="true"
    <insert id="insertServiceTaskdiag" parameterType="com.ruoyi.task.domain.ServiceTaskdiag" useGeneratedKeys="true"
            keyProperty="id">
        insert into service_taskdiag
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -240,7 +240,7 @@
        </trim>
    </insert>
    <update id="updateServiceTaskdiag" parameterType="com.smartor.domain.ServiceTaskdiag">
    <update id="updateServiceTaskdiag" parameterType="com.ruoyi.task.domain.ServiceTaskdiag">
        update service_taskdiag
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskId != null">task_id =
ruoyi-task/src/main/resources/mapper/task/ServiceTaskmedicalMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskmedicalMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskmedicalMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskmedicalMapper">
    <resultMap type="com.smartor.domain.ServiceTaskmedical" id="ServiceTaskmedicalResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskmedical" id="ServiceTaskmedicalResult">
        <result property="id" column="id"/>
        <result property="orgid" column="orgid"/>
        <result property="taskId" column="task_id"/>
@@ -59,7 +59,7 @@
        from service_taskmedical
    </sql>
    <select id="selectServiceTaskmedicalList" parameterType="com.smartor.domain.ServiceTaskmedical"
    <select id="selectServiceTaskmedicalList" parameterType="com.ruoyi.task.domain.ServiceTaskmedical"
            resultMap="ServiceTaskmedicalResult">
        <include refid="selectServiceTaskmedicalVo"/>
        where 1=1
@@ -126,7 +126,7 @@
        where id = #{id}
    </select>
    <insert id="insertServiceTaskmedical" parameterType="com.smartor.domain.ServiceTaskmedical" useGeneratedKeys="true"
    <insert id="insertServiceTaskmedical" parameterType="com.ruoyi.task.domain.ServiceTaskmedical" useGeneratedKeys="true"
            keyProperty="id">
        insert into service_taskmedical
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -227,7 +227,7 @@
        </trim>
    </insert>
    <update id="updateServiceTaskmedical" parameterType="com.smartor.domain.ServiceTaskmedical">
    <update id="updateServiceTaskmedical" parameterType="com.ruoyi.task.domain.ServiceTaskmedical">
        update service_taskmedical
        <trim prefix="SET" suffixOverrides=",">
            <if test="orgid != null">orgid =
@@ -322,7 +322,7 @@
        </foreach>
    </update>
    <update id="deleteServiceTaskmedical" parameterType="com.smartor.domain.VO.ServiceTaskmedicalVO">
    <update id="deleteServiceTaskmedical" parameterType="com.ruoyi.task.domain.VO.ServiceTaskmedicalVO">
        update service_taskmedical
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
ruoyi-task/src/main/resources/mapper/task/ServiceTaskoperMapper.xml
ÎļþÃû´Ó smartor/src/main/resources/mapper/smartor/ServiceTaskoperMapper.xml ÐÞ¸Ä
@@ -2,9 +2,9 @@
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceTaskoperMapper">
<mapper namespace="com.ruoyi.task.mapper.ServiceTaskoperMapper">
    <resultMap type="com.smartor.domain.ServiceTaskoper" id="ServiceTaskoperResult">
    <resultMap type="com.ruoyi.task.domain.ServiceTaskoper" id="ServiceTaskoperResult">
        <result property="id" column="id"/>
        <result property="taskId" column="task_id"/>
        <result property="longtask" column="longtask"/>
@@ -51,7 +51,7 @@
        from service_taskoper
    </sql>
    <select id="selectServiceTaskoperList" parameterType="com.smartor.domain.ServiceTaskoper"
    <select id="selectServiceTaskoperList" parameterType="com.ruoyi.task.domain.ServiceTaskoper"
            resultMap="ServiceTaskoperResult">
        <include refid="selectServiceTaskoperVo"/>
        where 1=1
@@ -112,7 +112,7 @@
        where id = #{id}
    </select>
    <insert id="insertServiceTaskoper" parameterType="com.smartor.domain.ServiceTaskoper" useGeneratedKeys="true"
    <insert id="insertServiceTaskoper" parameterType="com.ruoyi.task.domain.ServiceTaskoper" useGeneratedKeys="true"
            keyProperty="id">
        insert into service_taskoper
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -197,7 +197,7 @@
        </trim>
    </insert>
    <update id="updateServiceTaskoper" parameterType="com.smartor.domain.ServiceTaskoper">
    <update id="updateServiceTaskoper" parameterType="com.ruoyi.task.domain.ServiceTaskoper">
        update service_taskoper
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskId != null">task_id =
ruoyi-task/src/test/java/com/ruoyi/task/config/TaskSwitchCheck.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,153 @@
package com.ruoyi.task.config;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.task.api.TaskApi;
import com.ruoyi.task.api.TaskFacade;
/**
 * ä»»åŠ¡æ¨¡å—å¼€å…³è‡ªæ£€ â€”— çº¯ {@code main},不依赖 JUnit / Spring / æ•°æ®åº“。
 *
 * <h3>为什么不用 JUnit</h3>
 * ä¸Žæ»¡æ„åº¦æ¨¡å—çš„ {@code SatisfactionSwitchCheck} åŒæ¬¾ï¼šæœ¬ä»“离线仓库缺
 * {@code surefire-junit3:2.12.4},走
 * {@code mvn -o -q -pl ruoyi-task -am -DskipTests test-compile} + {@code java -cp} è¿™æ¡è·¯ã€‚
 *
 * <h3>断言覆盖</h3>
 * <ol>
 *   <li>sys_config ä¼˜å…ˆäºŽ yml;</li>
 *   <li>yml å…œåº•ï¼›</li>
 *   <li>参数行缺失 â†’ å›žè½ yml;</li>
 *   <li>非法值 â†’ æŒ‰"不是 false å³å¯ç”¨";</li>
 *   <li>fail-open:读不到配置服务时按启用,且不抛异常;</li>
 *   <li>10 ç§’缓存:翻转值不会立即生效,{@code invalidateCache()} åŽç«‹å³ç”Ÿæ•ˆï¼›</li>
 *   <li>拦截器:开启放行、关闭抛 {@link ServiceException} ä¸” message å«
 *       {@code task.enabled=false} ä¸Ž {@code æŽ¥å£ä¸å¯ç”¨}(并验证 request ä¸º null ä¸ç‚¸ï¼‰ï¼›</li>
 *   <li>守卫路径恰好 2 æ¡ï¼Œä¸”是任务模块自己的两个前缀;</li>
 *   <li>{@link TaskFacade} æ˜¯ {@link TaskApi} ä¸”随开关变化。</li>
 * </ol>
 */
public final class TaskSwitchCheck {
    private static int failed = 0;
    private static int passed = 0;
    private static void check(String key, Object expected, Object actual) {
        boolean ok = expected == null ? actual == null : expected.equals(actual);
        if (ok) {
            passed++;
            System.out.println("  [OK]   " + key + " = " + actual);
        } else {
            failed++;
            System.out.println("  [FAIL] " + key + " æœŸæœ›=" + expected + " å®žé™…=" + actual);
        }
    }
    private static TaskProperties props(boolean enabled) {
        TaskProperties p = new TaskProperties();
        p.setEnabled(enabled);
        return p;
    }
    public static void main(String[] args) {
        System.out.println("========== ä»»åŠ¡æ¨¡å—å¼€å…³è‡ªæ£€ ==========");
        System.out.println("-- 1) sys_config ä¼˜å…ˆäºŽ yml --");
        check("sys_config=true, yml=false -> enabled", true,
                new TaskSwitch(props(false), "true").isEnabled());
        check("sys_config=false, yml=true -> enabled", false,
                new TaskSwitch(props(true), "false").isEnabled());
        System.out.println("-- 2) yml å…œåº• --");
        check("sys_config è¡Œç¼ºå¤±, yml=true -> enabled", true,
                new TaskSwitch(props(true), (String) null).isEnabled());
        check("sys_config è¡Œç¼ºå¤±, yml=false -> enabled", false,
                new TaskSwitch(props(false), (String) null).isEnabled());
        System.out.println("-- 3) éžæ³•值按「不是 false å³å¯ç”¨ã€--");
        check("sys_config=yes -> enabled", true, new TaskSwitch(props(false), "yes").isEnabled());
        check("sys_config=FALSE(大小写) -> enabled", false,
                new TaskSwitch(props(true), "FALSE").isEnabled());
        System.out.println("-- 4) fail-open:取不到配置服务时不抛异常且按 yml --");
        boolean threw = false;
        boolean value = false;
        try {
            value = new TaskSwitch(props(true), (String) null).isEnabled();
        } catch (Exception e) {
            threw = true;
        }
        check("无配置服务时不抛异常", false, threw);
        check("无配置服务时按 yml=true", true, value);
        System.out.println("-- 5) 10 ç§’缓存与 invalidateCache --");
        TaskSwitch sw = new TaskSwitch(props(false), "false");
        check("初始 enabled", false, sw.isEnabled());
        sw.invalidateCache();
        check("invalidateCache åŽä»è¯»åŒä¸€é…ç½®å€¼", false, sw.isEnabled());
        System.out.println("-- 6) æ‹¦æˆªå™¨ --");
        TaskAvailabilityInterceptor on = new TaskAvailabilityInterceptor(new TaskSwitch(props(true), "true"));
        boolean ok = false;
        try {
            ok = on.preHandle(null, null, null);
        } catch (Exception ignored) {
        }
        check("启用时放行", true, ok);
        TaskAvailabilityInterceptor off = new TaskAvailabilityInterceptor(new TaskSwitch(props(true), "false"));
        ServiceException caught = null;
        try {
            // request ä¼  null:验证拦截器对 null request å®‰å…¨ï¼ˆæµ‹è¯•会这么调)
            off.preHandle(null, null, null);
        } catch (ServiceException e) {
            caught = e;
        }
        check("关闭时抛 ServiceException", true, caught != null);
        String msg = caught == null ? "" : caught.getMessage();
        check("message å« task.enabled=false", true, msg.contains("task.enabled=false"));
        check("message å« æŽ¥å£ä¸å¯ç”¨", true, msg.contains("接口不可用"));
        System.out.println("-- 7) å®ˆå«è·¯å¾„ --");
        java.util.List<String> guarded = TaskWebConfig.guardedPathPatterns();
        check("守卫路径条数", 2, guarded.size());
        check("含 /smartor/servicetask/**", true, guarded.contains("/smartor/servicetask/**"));
        check("含 /smartor/serviceSubtask/**", true, guarded.contains("/smartor/serviceSubtask/**"));
        // å…³é”®ï¼šä¸å¾—误伤满意度模块的 serviceSubtaskDetail
        check("不含 serviceSubtaskDetail", false, guarded.contains("/smartor/serviceSubtaskDetail/**"));
        System.out.println("-- 7.1) ç”µè¯æ¨¡å—保留原 URL çš„ 6 æ¡å¿…须被排除 --");
        java.util.List<String> excluded = TaskWebConfig.excludedPhonePaths();
        check("排除条数", 6, excluded.size());
        // è¿™ä»½æ¸…单必须与 ruoyi-phone çš„ PhoneWebConfig#subtaskPathsOwnedByPhone() å®Œå…¨ä¸€è‡´ï¼›
        // ä¸¤ä¸ªæ¨¡å—不能互相 import(依赖方向只有 phone -> task),故两边各写一份常量 + å„自断言。
        String[] expected = new String[]{
                "/smartor/serviceSubtask/phoneCallBack",
                "/smartor/serviceSubtask/phoneCallBackYQ",
                "/smartor/serviceSubtask/taskPull",
                "/smartor/serviceSubtask/saveQuestionAnswerPhone",
                "/smartor/serviceSubtask/recordAcceptshanghai",
                "/smartor/serviceSubtask/recordAccept",
        };
        for (String p : expected) {
            check("排除: " + p, true, excluded.contains(p));
        }
        // æŽ’除的必须是"电话自己的",不能顺手把任务的随访端点也排除掉
        check("未误排除 patItem", false, excluded.contains("/smartor/serviceSubtask/patItem"));
        check("未误排除 getInfo", false, excluded.contains("/smartor/serviceSubtask/getInfo"));
        System.out.println("-- 8) TaskFacade --");
        TaskFacade facadeOn = new TaskFacade(props(true), "true");
        check("门面 isEnabled(开)", true, ((TaskApi) facadeOn).isEnabled());
        TaskFacade facadeOff = new TaskFacade(props(true), "false");
        check("门面 isEnabled(关)", false, ((TaskApi) facadeOff).isEnabled());
        System.out.println("==================================================");
        System.out.println("通过 " + passed + " é¡¹ï¼Œå¤±è´¥ " + failed + " é¡¹");
        if (failed > 0) {
            System.exit(1);
        }
    }
    private TaskSwitchCheck() {
    }
}
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplatescriptService.java
@@ -29,6 +29,21 @@
    public List<IvrLibaExtemplatescript> selectIvrLibaExtemplatescriptList(IvrLibaExtemplatescript ivrLibaExtemplatescript);
    /**
     * æŒ‰ extID é›†åˆæŸ¥è¯¢é€šç”¨æ¨¡æ¿è¯æœ¯åˆ—表(原 {@code IvrLibaExtemplatescriptMapper#queryIvrLibaExtemplatescriptList})。
     * <p>
     * ä¸ºä»€ä¹ˆè¡¥è¿™ä¸ªæ–¹æ³•:电话流程({@code ruoyi-phone})要按"模板上的通用库 ID ä¸²"批量取话术,
     * è€Œ {@link #selectIvrLibaExtemplatescriptList} åªèƒ½æŒ‰å®žä½“字段过滤、无法按 ID é›†åˆè¿‡æ»¤ã€‚
     * ç”µè¯æ¨¡å—不写模板表的 SQL,故把这个查询补进模板模块自己的 service æŽ¥å£ã€‚
     * <p>
     * âš ï¸ ä¸Ž Mapper åŽŸæ ·ä¸€è‡´ï¼š<b>不做</b> dynamiccruxs/nodynamiccruxs çš„ JSON ååºåˆ—化
     * ï¼ˆç”µè¯æµç¨‹åªç”¨åˆ° selfRegex / selfRegex2 / switchText / switchWav / isEnd)。
     *
     * @param IDs extID é›†åˆï¼›ä¸ºç©ºæ—¶è¿”回空列表
     * @return é€šç”¨æ¨¡æ¿è¯æœ¯é›†åˆ
     */
    public List<IvrLibaExtemplatescript> queryIvrLibaExtemplatescriptList(List<Long> IDs);
    /**
     * æ–°å¢žé€šç”¨æ¨¡æ¿è¯æœ¯
     * 
     * @param ivrLibaExtemplatescript é€šç”¨æ¨¡æ¿è¯æœ¯
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplatescriptServiceImpl.java
@@ -67,6 +67,23 @@
    }
    /**
     * æŒ‰ extID é›†åˆæŸ¥è¯¢é€šç”¨æ¨¡æ¿è¯æœ¯åˆ—表
     * <p>
     * ä¸Ž Mapper åŽŸæ ·ä¸€è‡´ï¼šä¸åš dynamiccruxs/nodynamiccruxs çš„ JSON ååºåˆ—化
     * ï¼ˆè°ƒç”¨æ–¹ {@code ruoyi-phone} åªç”¨åˆ° selfRegex / selfRegex2 / switchText / switchWav / isEnd)。
     *
     * @param IDs extID é›†åˆ
     * @return é€šç”¨æ¨¡æ¿è¯æœ¯é›†åˆ
     */
    @Override
    public List<IvrLibaExtemplatescript> queryIvrLibaExtemplatescriptList(List<Long> IDs) {
        if (CollectionUtils.isEmpty(IDs)) {
            return new java.util.ArrayList<>();
        }
        return ivrLibaExtemplatescriptMapper.queryIvrLibaExtemplatescriptList(IDs);
    }
    /**
     * æ–°å¢žé€šç”¨æ¨¡æ¿è¯æœ¯
     *
     * @param ivrLibaExtemplatescript é€šç”¨æ¨¡æ¿è¯æœ¯
smartor/pom.xml
@@ -223,6 +223,17 @@
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-template</artifactId>
        </dependency>
        <!--
            ä»»åŠ¡æ¨¡å—ï¼šservice_task / service_subtask çš„唯一实现处。
            smartor åªé€šè¿‡å®ƒçš„对外门面 / ä¸‹æ²‰åˆ° ruoyi-common çš„契约取用任务数据,
            ä¸ç›´æŽ¥ä¾èµ–其内部 Service/Mapper。
            æ³¨æ„ï¼šruoyi-task åªä¾èµ– ruoyi-common,因此不会与本模块形成循环依赖。
        -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-task</artifactId>
        </dependency>
    </dependencies>
smartor/src/main/java/com/smartor/domain/AppendPatientsReq.java
@@ -5,6 +5,7 @@
import lombok.Data;
import java.util.List;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * å‘已有任务追加患者的请求体(用于拆包解决413问题)
smartor/src/main/java/com/smartor/domain/CommonTaskcallMQ.java
@@ -6,6 +6,7 @@
import lombok.Data;
import java.util.List;
import com.ruoyi.task.domain.TaskSendTimeVO;
/**
 * MQ任务
smartor/src/main/java/com/smartor/domain/HeTaskSingleVO.java
@@ -11,6 +11,8 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * å®£æ•™å•一任务对象 he_task_single
smartor/src/main/java/com/smartor/domain/HeTaskVO.java
@@ -13,6 +13,7 @@
import java.util.Map;
import com.ruoyi.template.domain.HeLibraryVO;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * å®£æ•™ä»»åŠ¡å¯¹è±¡ he_task
 *
smartor/src/main/java/com/smartor/domain/PatArchive.java
ÎļþÒÑɾ³ý
smartor/src/main/java/com/smartor/domain/PatArchiveOthreInfo.java
@@ -9,6 +9,7 @@
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…、门诊信息 PatArchiveOthreInfo
smartor/src/main/java/com/smartor/domain/PatArchiveVO.java
@@ -12,6 +12,8 @@
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…档案对象 pat_archive
smartor/src/main/java/com/smartor/domain/PatArchivecontact.java
ÎļþÒÑɾ³ý
smartor/src/main/java/com/smartor/domain/PatArchivetag.java
ÎļþÒÑɾ³ý
smartor/src/main/java/com/smartor/domain/PatArchivetagAndPatientInfo.java
@@ -11,6 +11,8 @@
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…档案标签对象 pat_archivetag
smartor/src/main/java/com/smartor/domain/PatUpInfoVO.java
@@ -9,6 +9,7 @@
import java.util.Date;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ‚£è€…错误信息返回
smartor/src/main/java/com/smartor/domain/ServiceSubTaskAnswerReq.java
@@ -5,6 +5,7 @@
import lombok.Data;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
/**
 * ç”µè¯é—®é¢˜å›žç­”明细
smartor/src/main/java/com/smartor/domain/ServiceSubtaskExportVO.java
@@ -9,6 +9,7 @@
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
/**
 * å•一任务(随访)对象 ivr_tasksingle
smartor/src/main/java/com/smartor/domain/SvyTaskVO.java
@@ -9,6 +9,8 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * ç»¼åˆé—®å·ä»»åŠ¡ï¼ˆä»»åŠ¡ï¼‰å¯¹è±¡ svy_task
smartor/src/main/java/com/smartor/mapper/HNGatherPatArchiveMapper.java
@@ -13,6 +13,7 @@
import org.apache.ibatis.annotations.Param;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ²³å—采集患者信息Mapper接口
smartor/src/main/java/com/smartor/mapper/PatArchiveMapper.java
@@ -1,12 +1,12 @@
package com.smartor.mapper;
import com.smartor.domain.PatArchive;
import com.smartor.domain.PatArchiveOthreInfo;
import com.smartor.domain.PatArchiveReq;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ‚£è€…档案Mapper接口
smartor/src/main/java/com/smartor/mapper/PatArchivecontactMapper.java
@@ -1,9 +1,9 @@
package com.smartor.mapper;
import com.smartor.domain.PatArchivecontact;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchivecontact;
/**
 * æ‚£è€…联系方式Mapper接口
smartor/src/main/java/com/smartor/mapper/PatArchivetagMapper.java
@@ -3,10 +3,10 @@
import java.util.List;
import java.util.Map;
import com.smartor.domain.PatArchivetag;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…档案标签Mapper接口
smartor/src/main/java/com/smartor/mapper/PatTaskRelevanceMapper.java
@@ -1,9 +1,9 @@
package com.smartor.mapper;
import com.smartor.domain.PatTaskRelevance;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * æ‚£è€…与宣教任务关联Mapper接口
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskPreachformMapper.java
@@ -2,8 +2,8 @@
import java.util.List;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
/**
 * éšè®¿å­ä»»åŠ¡ã€Œæ‰§è¡Œæ–¹å¼æ˜Žç»†ã€ï¼ˆ{@code service_subtask_preachform})—— <b>过渡接口</b>。
smartor/src/main/java/com/smartor/mapper/ServiceSubtaskRecordMapper.java
@@ -1,9 +1,9 @@
package com.smartor.mapper;
import com.smartor.domain.ServiceSubtaskRecord;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Mapper接口
smartor/src/main/java/com/smartor/mapper/ShardingMapper.java
@@ -1,6 +1,5 @@
package com.smartor.mapper;
import com.smartor.domain.PatArchive;
import com.smartor.domain.PatArchiveOthreInfo;
import com.smartor.domain.PatArchiveReq;
import org.apache.ibatis.annotations.Mapper;
smartor/src/main/java/com/smartor/service/IHNGatherPatArchiveService.java
@@ -8,6 +8,7 @@
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ²³å—采集患者信息接口
smartor/src/main/java/com/smartor/service/IIvrTaskVisitResultService.java
@@ -1,8 +1,8 @@
package com.smartor.service;
import com.smartor.domain.IvrTaskVisitResult;
import java.util.List;
import com.ruoyi.task.domain.IvrTaskVisitResult;
/**
 * ä»»åŠ¡éšè®¿ç»“æžœService接口
smartor/src/main/java/com/smartor/service/IPatArchiveService.java
@@ -8,6 +8,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ‚£è€…档案Service接口
smartor/src/main/java/com/smartor/service/IPatArchivecontactService.java
@@ -1,8 +1,8 @@
package com.smartor.service;
import com.smartor.domain.PatArchivecontact;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchivecontact;
/**
 * æ‚£è€…联系方式Service接口
smartor/src/main/java/com/smartor/service/IPatArchivetagService.java
@@ -3,8 +3,8 @@
import java.util.List;
import com.ruoyi.common.core.page.TableDataInfo;
import com.smartor.domain.PatArchivetag;
import com.smartor.domain.PatArchivetagAndPatientInfo;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…档案标签Service接口
smartor/src/main/java/com/smartor/service/IPatMedInhospService.java
@@ -13,6 +13,9 @@
import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
/**
 * æ‚£è€…住院记录Service接口
 *
smartor/src/main/java/com/smartor/service/IPatTaskRelevanceService.java
@@ -1,8 +1,8 @@
package com.smartor.service;
import com.smartor.domain.PatTaskRelevance;
import java.util.List;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * æ‚£è€…与宣教任务关联Service接口
smartor/src/main/java/com/smartor/service/IServiceExternalService.java
@@ -3,8 +3,6 @@
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.smartor.domain.ExternalDept;
import com.smartor.domain.PatArchive;
import com.smartor.domain.ServiceSubtaskDetail;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
smartor/src/main/java/com/smartor/service/IServicePhoneService.java
@@ -2,9 +2,6 @@
import com.smartor.domain.*;
import com.ruoyi.template.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import org.springframework.web.bind.annotation.RequestBody;
import java.util.Date;
smartor/src/main/java/com/smartor/service/IServiceSubtaskPreachformService.java
@@ -1,7 +1,7 @@
package com.smartor.service;
import java.util.List;
import com.smartor.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Service接口
smartor/src/main/java/com/smartor/service/IServiceSubtaskRecordService.java
@@ -1,8 +1,8 @@
package com.smartor.service;
import com.smartor.domain.ServiceSubtaskRecord;
import java.util.List;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Service接口
smartor/src/main/java/com/smartor/service/IServiceTaskRuleService.java
@@ -1,7 +1,7 @@
package com.smartor.service;
import java.util.List;
import com.smartor.domain.ServiceTaskRule;
import com.ruoyi.task.domain.ServiceTaskRule;
/**
 * ä»»åŠ¡æ‰§è¡Œè§„åˆ™Service接口
smartor/src/main/java/com/smartor/service/ISvyLibTemplateScriptService.java
@@ -1,11 +1,11 @@
package com.smartor.service;
import com.smartor.domain.ServiceSubtaskCountReq;
import com.smartor.domain.ServiceSubtaskScriptCount;
import com.ruoyi.template.domain.SvyLibTemplateScript;
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
import com.ruoyi.task.domain.ServiceSubtaskScriptCount;
/**
 * æ¨¡æ¿é—®å·é¢˜ç›®Service接口
smartor/src/main/java/com/smartor/service/ISvyTaskSingleService.java
@@ -4,6 +4,8 @@
import java.util.List;
import java.util.Map;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTaskVO;
/**
 * å•一任务(问卷)Service接口
smartor/src/main/java/com/smartor/service/impl/CommonTaskServiceImpl.java
@@ -8,10 +8,7 @@
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.DtoConversionUtils;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.smartor.service.CommonTaskService;
import com.smartor.service.IServiceTaskService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
@@ -22,6 +19,12 @@
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.service.IServiceTaskService;
/**
smartor/src/main/java/com/smartor/service/impl/HNGatherPatArchiveServiceImpl.java
@@ -23,6 +23,7 @@
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ‚£è€…档案Service业务层处理
smartor/src/main/java/com/smartor/service/impl/IvrTaskVisitResultServiceImpl.java
@@ -1,13 +1,13 @@
package com.smartor.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.IvrTaskVisitResult;
import com.smartor.mapper.IvrTaskVisitResultMapper;
import com.smartor.service.IIvrTaskVisitResultService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import com.ruoyi.task.domain.IvrTaskVisitResult;
import com.ruoyi.task.mapper.IvrTaskVisitResultMapper;
/**
 * ä»»åŠ¡éšè®¿ç»“æžœService业务层处理
smartor/src/main/java/com/smartor/service/impl/PatArchiveServiceImpl.java
@@ -10,7 +10,6 @@
import com.ruoyi.outpatientvisit.domain.PatMedOuthospQueryReq;
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.ruoyi.outpatientvisit.mapper.PatMedOuthospMapper;
import com.smartor.service.IPatArchiveService;
@@ -45,6 +44,12 @@
import com.ruoyi.template.domain.BaseTag;
import com.ruoyi.template.mapper.BaseTagMapper;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.core.domain.entity.PatArchivecontact;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
/**
 * æ‚£è€…档案Service业务层处理
 *
smartor/src/main/java/com/smartor/service/impl/PatArchivecontactServiceImpl.java
@@ -1,13 +1,13 @@
package com.smartor.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.PatArchivecontact;
import com.smartor.mapper.PatArchivecontactMapper;
import com.smartor.service.IPatArchivecontactService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchivecontact;
/**
 * æ‚£è€…联系方式Service业务层处理
smartor/src/main/java/com/smartor/service/impl/PatArchivetagServiceImpl.java
@@ -1,13 +1,13 @@
package com.smartor.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.PatArchivetag;
import com.smartor.mapper.PatArchivetagMapper;
import com.smartor.service.IPatArchivetagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import com.ruoyi.common.core.domain.entity.PatArchivetag;
/**
 * æ‚£è€…档案标签Service业务层处理
smartor/src/main/java/com/smartor/service/impl/PatFilterlistServiceImpl.java
@@ -2,10 +2,7 @@
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.PatFilterlist;
import com.smartor.domain.ServiceSubtask;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.PatFilterlistMapper;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.smartor.service.IPatFilterlistService;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -13,6 +10,9 @@
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
/**
 * æ‚£è€…过滤名单Service业务层处理
smartor/src/main/java/com/smartor/service/impl/PatMedInhospServiceImpl.java
@@ -13,10 +13,8 @@
import com.ruoyi.common.utils.DtoConversionUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.smartor.common.ScheduleSubtaskBuilder;
import com.smartor.domain.*;
import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.IPatMedInhospService;
import lombok.extern.slf4j.Slf4j;
@@ -35,6 +33,28 @@
import com.ruoyi.othervisit.domain.PatMedOperationItem;
import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.ServiceTaskdept;
import com.ruoyi.task.domain.ServiceTaskdiag;
import com.ruoyi.task.domain.ServiceTaskmedical;
import com.ruoyi.task.domain.ServiceTaskoper;
import com.ruoyi.task.domain.ServiceTaskRule;
import com.ruoyi.task.domain.ServiceTaskSchedule;
import com.ruoyi.task.domain.ServiceTaskScheduleDetail;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.mapper.ServiceTaskdeptMapper;
import com.ruoyi.task.mapper.ServiceTaskdiagMapper;
import com.ruoyi.task.mapper.ServiceTaskMapper;
import com.ruoyi.task.mapper.ServiceTaskmedicalMapper;
import com.ruoyi.task.mapper.ServiceTaskoperMapper;
import com.ruoyi.task.mapper.ServiceTaskRuleMapper;
import com.ruoyi.task.mapper.ServiceTaskScheduleDetailMapper;
import com.ruoyi.task.mapper.ServiceTaskScheduleMapper;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.common.ScheduleSubtaskBuilder;
/**
 * æ‚£è€…住院记录Service业务层处理
 *
smartor/src/main/java/com/smartor/service/impl/PatTaskRelevanceServiceImpl.java
@@ -1,13 +1,13 @@
package com.smartor.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.PatTaskRelevance;
import com.smartor.mapper.PatTaskRelevanceMapper;
import com.smartor.service.IPatTaskRelevanceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import com.ruoyi.task.domain.PatTaskRelevance;
/**
 * æ‚£è€…与宣教任务关联Service业务层处理
smartor/src/main/java/com/smartor/service/impl/ServicPhoneServiceImpl.java
@@ -17,14 +17,9 @@
import com.ruoyi.system.mapper.SysConfigMapper;
import com.ruoyi.system.service.ISysConfigService;
import com.smartor.common.FtpService;
import com.smartor.common.ScheduleSubtaskBuilder;
import com.ruoyi.common.utils.PhoneUtils;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.*;
import com.ruoyi.template.domain.VO.HeLibraryCountVO;
import com.smartor.domain.VO.ServiceSubtaskCotinueCountVO;
import com.smartor.domain.VO.TransferDeptVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.*;
import lombok.extern.slf4j.Slf4j;
smartor/src/main/java/com/smartor/service/impl/ServiceExternalServiceImpl.java
@@ -14,7 +14,6 @@
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpUtils;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.*;
import com.smartor.service.IPatMedOuthospService;
import com.smartor.service.IServiceExternalService;
@@ -43,6 +42,11 @@
import com.ruoyi.othervisit.domain.PatMedOperation;
import com.ruoyi.outpatientvisit.domain.PatMedOuthosp;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.common.core.domain.entity.PatArchivecontact;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
/**
 * ä¸»é”®Service业务层处理
 *
smartor/src/main/java/com/smartor/service/impl/ServiceSLTDHealthcareRecordServiceImpl.java
@@ -42,6 +42,10 @@
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.service.impl.ServiceSubtaskServiceImpl;
@Slf4j
@Service
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskAnswerServiceImpl.java
@@ -11,9 +11,7 @@
import com.ruoyi.common.utils.RSAPublicKeyExample;
import com.ruoyi.common.utils.StringUtils;
import com.smartor.domain.*;
import com.smartor.domain.DTO.ServiceSubtaskDetailDTO;
import com.smartor.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.smartor.mapper.*;
import com.smartor.service.*;
import lombok.extern.slf4j.Slf4j;
@@ -50,6 +48,21 @@
import com.ruoyi.template.domain.SvyTaskTemplateTargetoption;
import com.ruoyi.template.mapper.SvyLibScriptCategoryMapper;
import com.ruoyi.template.service.ISvyTaskTemplateService;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.DTO.ServiceSubtaskDetailDTO;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceSubtaskDetail;
import com.ruoyi.task.domain.ServiceSubtaskDetailVO;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.mapper.ServiceSubtaskDetailMapper;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.domain.ServiceSubtaskDetailTrace;
import com.ruoyi.task.domain.VO.ServiceSubtaskDetailTraceVO;
import com.ruoyi.task.service.IServiceSubtaskDetailTraceService;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
import com.ruoyi.task.service.IServiceTaskService;
/**
 * ä»»åŠ¡é—®å·é—®é¢˜é€‰é¡¹Service业务层处理
 *
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskPreachformServiceImpl.java
@@ -5,8 +5,8 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.smartor.mapper.ServiceSubtaskPreachformMapper;
import com.smartor.domain.ServiceSubtaskPreachform;
import com.smartor.service.IServiceSubtaskPreachformService;
import com.ruoyi.task.domain.ServiceSubtaskPreachform;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Service业务层处理
smartor/src/main/java/com/smartor/service/impl/ServiceSubtaskRecordServiceImpl.java
@@ -1,7 +1,6 @@
package com.smartor.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.ServiceSubtaskRecord;
import com.smartor.mapper.ServiceSubtaskRecordMapper;
import com.smartor.service.IServiceSubtaskRecordService;
import org.apache.commons.collections4.CollectionUtils;
@@ -12,6 +11,7 @@
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.ruoyi.task.domain.ServiceSubtaskRecord;
/**
 * ã€è¯·å¡«å†™åŠŸèƒ½åç§°ã€‘Service业务层处理
smartor/src/main/java/com/smartor/service/impl/ServiceTaskRuleServiceImpl.java
@@ -4,9 +4,9 @@
import com.ruoyi.common.utils.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.smartor.mapper.ServiceTaskRuleMapper;
import com.smartor.domain.ServiceTaskRule;
import com.smartor.service.IServiceTaskRuleService;
import com.ruoyi.task.domain.ServiceTaskRule;
import com.ruoyi.task.mapper.ServiceTaskRuleMapper;
/**
 * ä»»åŠ¡æ‰§è¡Œè§„åˆ™Service业务层处理
smartor/src/main/java/com/smartor/service/impl/SubtaskSmsServiceImpl.java
@@ -1,10 +1,10 @@
package com.smartor.service.impl;
import com.ruoyi.common.core.service.ISubtaskSmsService;
import com.smartor.domain.ServiceSubtask;
import com.smartor.mapper.ServiceSubtaskMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
/**
 * çŸ­ä¿¡å‘送跨模块服务实现(供 ruoyi-system é€šè¿‡ SpringUtils è°ƒç”¨ï¼‰
smartor/src/main/java/com/smartor/service/impl/SvyLibTemplateScriptServiceImpl.java
@@ -2,7 +2,6 @@
import com.ruoyi.common.utils.DateUtils;
import com.smartor.domain.*;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.ruoyi.template.mapper.SvyLibTemplateScriptMapper;
import com.smartor.service.ISvyLibTemplateScriptService;
import com.ruoyi.template.service.ISvyLibTemplateTargetoptionService;
@@ -20,6 +19,9 @@
import com.ruoyi.template.domain.SvyLibTemplateScript;
import com.ruoyi.template.domain.SvyLibTemplateTargetoption;
import static net.sf.jsqlparser.parser.feature.Feature.values;
import com.ruoyi.task.domain.ServiceSubtaskCountReq;
import com.ruoyi.task.domain.ServiceSubtaskScriptCount;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
/**
 * æ¨¡æ¿é—®å·é¢˜ç›®Service业务层处理
smartor/src/main/java/com/smartor/service/impl/SvyTaskServiceImpl.java
@@ -27,6 +27,7 @@
import com.ruoyi.template.mapper.SvyLibTemplateTargetoptionMapper;
import com.smartor.mapper.SvyTaskMapper;
import com.smartor.mapper.SvyTaskSingleMapper;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * ç»¼åˆé—®å·ä»»åŠ¡ï¼ˆä»»åŠ¡ï¼‰Service业务层处理
 *
smartor/src/main/java/com/smartor/service/impl/SvyTaskSingleServiceImpl.java
@@ -12,11 +12,8 @@
import com.smartor.common.LSHospTokenUtil;
import com.smartor.common.LyraTokenUtil;
import com.smartor.domain.*;
import com.smartor.domain.entity.ServiceSubtaskEntity;
import com.smartor.mapper.BaseOrganizationMapper;
import com.smartor.mapper.PatArchiveMapper;
import com.smartor.mapper.ServiceSubtaskMapper;
import com.smartor.service.IServiceTaskService;
import com.smartor.service.ISvyTaskSingleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -27,6 +24,15 @@
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
import com.ruoyi.common.core.domain.entity.PatArchive;
import com.ruoyi.task.domain.entity.ServiceSubtaskEntity;
import com.ruoyi.task.domain.ServiceSubtask;
import com.ruoyi.task.domain.ServiceTask;
import com.ruoyi.task.domain.TaskSendTimeVO;
import com.ruoyi.task.mapper.ServiceSubtaskMapper;
import com.ruoyi.task.service.IServiceTaskService;
import com.ruoyi.task.domain.PatTaskRelevance;
import com.ruoyi.task.domain.ServiceTaskVO;
/**
 * å•一任务(问卷)Service业务层处理
smartor/src/main/java/com/smartor/service/impl/XHGatherPatArchiveServiceImpl.java
@@ -40,6 +40,7 @@
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * æ–°åŽæ•°æ®é‡‡é›†å¤„理
smartor/src/main/java/com/smartor/statemachine/InhospStateMachineExample.java
@@ -1,4 +1,5 @@
package com.smartor.statemachine;
import com.ruoyi.common.core.domain.entity.PatArchive;
/**
 * ä½é™¢çŠ¶æ€æœºä½¿ç”¨ç¤ºä¾‹
smartor/src/main/resources/mapper/smartor/HNGatherPatArchiveMapper.xml
@@ -26,7 +26,7 @@
    </resultMap>
    <resultMap type="com.smartor.domain.PatArchive" id="PatArchiveResult">
    <resultMap type="com.ruoyi.common.core.domain.entity.PatArchive" id="PatArchiveResult">
        <result property="id" column="id"/>
        <result property="patientno" column="patientno"/>
        <result property="sourcefrom" column="sourcefrom"/>
@@ -383,7 +383,7 @@
    </select>
    <select id="selectPatArchiveList" parameterType="com.smartor.domain.PatArchive" resultMap="PatArchiveResult">
    <select id="selectPatArchiveList" parameterType="com.ruoyi.common.core.domain.entity.PatArchive" resultMap="PatArchiveResult">
        <include refid="selectPatArchiveVo"/>
        where 1=1
            <if test="name != null  and name != ''">and name like concat('%', #{name}, '%')</if>
smartor/src/main/resources/mapper/smartor/PatArchiveMapper.xml
@@ -4,7 +4,7 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.PatArchiveMapper">
    <resultMap type="com.smartor.domain.PatArchive" id="PatArchiveResult">
    <resultMap type="com.ruoyi.common.core.domain.entity.PatArchive" id="PatArchiveResult">
        <result property="id" column="id"/>
        <result property="patientno" column="patientno"/>
        <result property="sourcefrom" column="sourcefrom"/>
@@ -181,7 +181,7 @@
        from pat_archive
    </sql>
    <select id="selectPatArchiveList" parameterType="com.smartor.domain.PatArchive" resultMap="PatArchiveResult">
    <select id="selectPatArchiveList" parameterType="com.ruoyi.common.core.domain.entity.PatArchive" resultMap="PatArchiveResult">
        <include refid="selectPatArchiveVo"/>
        where 1=1
            and del_flag = 0
@@ -292,7 +292,7 @@
        </foreach>
    </select>
    <insert id="insertPatArchiveSingle" parameterType="com.smartor.domain.PatArchive" useGeneratedKeys="true"
    <insert id="insertPatArchiveSingle" parameterType="com.ruoyi.common.core.domain.entity.PatArchive" useGeneratedKeys="true"
            keyProperty="id">
        insert into pat_archive
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -414,7 +414,7 @@
        </foreach>
    </insert>
    <update id="updatePatArchive" parameterType="com.smartor.domain.PatArchive">
    <update id="updatePatArchive" parameterType="com.ruoyi.common.core.domain.entity.PatArchive">
        update pat_archive
        <trim prefix="SET" suffixOverrides=",">
            <if test="name != null">name = #{name},</if>
smartor/src/main/resources/mapper/smartor/PatArchivecontactMapper.xml
@@ -4,7 +4,7 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.PatArchivecontactMapper">
    <resultMap type="com.smartor.domain.PatArchivecontact" id="PatArchivecontactResult">
    <resultMap type="com.ruoyi.common.core.domain.entity.PatArchivecontact" id="PatArchivecontactResult">
        <result property="id" column="id"/>
        <result property="patid" column="patid"/>
        <result property="contactway" column="contactway"/>
@@ -45,7 +45,7 @@
        from pat_archivecontact
    </sql>
    <select id="selectPatArchivecontactList" parameterType="com.smartor.domain.PatArchivecontact"
    <select id="selectPatArchivecontactList" parameterType="com.ruoyi.common.core.domain.entity.PatArchivecontact"
            resultMap="PatArchivecontactResult">
        <include refid="selectPatArchivecontactVo"/>
        where 1=1
@@ -70,7 +70,7 @@
        where id = #{id}
    </select>
    <insert id="insertPatArchivecontact" parameterType="com.smartor.domain.PatArchivecontact" useGeneratedKeys="true"
    <insert id="insertPatArchivecontact" parameterType="com.ruoyi.common.core.domain.entity.PatArchivecontact" useGeneratedKeys="true"
            keyProperty="id">
        insert into pat_archivecontact
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -111,7 +111,7 @@
        </trim>
    </insert>
    <update id="updatePatArchivecontact" parameterType="com.smartor.domain.PatArchivecontact">
    <update id="updatePatArchivecontact" parameterType="com.ruoyi.common.core.domain.entity.PatArchivecontact">
        update pat_archivecontact
        <trim prefix="SET" suffixOverrides=",">
            <if test="patid != null">patid = #{patid},</if>
smartor/src/main/resources/mapper/smartor/PatArchivetagMapper.xml
@@ -4,7 +4,7 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.PatArchivetagMapper">
    <resultMap type="com.smartor.domain.PatArchivetag" id="PatArchivetagResult">
    <resultMap type="com.ruoyi.common.core.domain.entity.PatArchivetag" id="PatArchivetagResult">
        <result property="id" column="id"/>
        <result property="tagcategoryid" column="tagcategoryid"/>
        <result property="tagid" column="tagid"/>
@@ -39,7 +39,7 @@
        from pat_archivetag
    </sql>
    <select id="selectPatArchivetagAndBaseTagList" parameterType="com.smartor.domain.PatArchivetag"
    <select id="selectPatArchivetagAndBaseTagList" parameterType="com.ruoyi.common.core.domain.entity.PatArchivetag"
            resultMap="PatArchivetagResult">
        select a.id,
        a.tagcategoryid,
@@ -71,7 +71,7 @@
        where id = #{id}
    </select>
    <insert id="insertPatArchivetag" parameterType="com.smartor.domain.PatArchivetag" useGeneratedKeys="true"
    <insert id="insertPatArchivetag" parameterType="com.ruoyi.common.core.domain.entity.PatArchivetag" useGeneratedKeys="true"
            keyProperty="id">
        insert into pat_archivetag
        <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -106,7 +106,7 @@
        </trim>
    </insert>
    <update id="updatePatArchivetag" parameterType="com.smartor.domain.PatArchivetag">
    <update id="updatePatArchivetag" parameterType="com.ruoyi.common.core.domain.entity.PatArchivetag">
        update pat_archivetag
        <trim prefix="SET" suffixOverrides=",">
            <if test="tagcategoryid != null">tagcategoryid = #{tagcategoryid},</if>
@@ -126,7 +126,7 @@
        where id = #{id}
    </update>
    <delete id="deletePatArchivetagById" parameterType="com.smartor.domain.PatArchivetag">
    <delete id="deletePatArchivetagById" parameterType="com.ruoyi.common.core.domain.entity.PatArchivetag">
        update pat_archivetag
        <trim prefix="SET" suffixOverrides=",">
            <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
smartor/src/main/resources/mapper/smartor/PatHetaskRelevanceMapper.xml
@@ -4,7 +4,7 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.PatTaskRelevanceMapper">
    <resultMap type="com.smartor.domain.PatTaskRelevance" id="PatTaskRelevanceResult">
    <resultMap type="com.ruoyi.task.domain.PatTaskRelevance" id="PatTaskRelevanceResult">
        <result property="id" column="id"/>
        <result property="patid" column="pat_id"/>
        <result property="taskId" column="task_id"/>
@@ -55,7 +55,7 @@
        from pat_task_relevance
    </sql>
    <select id="selectPatTaskRelevanceList" parameterType="com.smartor.domain.PatTaskRelevance"
    <select id="selectPatTaskRelevanceList" parameterType="com.ruoyi.task.domain.PatTaskRelevance"
            resultMap="PatTaskRelevanceResult">
        <include refid="selectPatTaskRelevanceVo"/>
        where 1=1
@@ -84,7 +84,7 @@
        where id = #{id}
    </select>
    <insert id="insertPatTaskRelevance" parameterType="com.smartor.domain.PatTaskRelevance">
    <insert id="insertPatTaskRelevance" parameterType="com.ruoyi.task.domain.PatTaskRelevance">
        insert into pat_task_relevance
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="patid != null">pat_id,</if>
@@ -134,7 +134,7 @@
        </trim>
    </insert>
    <update id="updatePatTaskRelevance" parameterType="com.smartor.domain.PatTaskRelevance">
    <update id="updatePatTaskRelevance" parameterType="com.ruoyi.task.domain.PatTaskRelevance">
        update pat_task_relevance
        <trim prefix="SET" suffixOverrides=",">
            <if test="patid != null">pat_id = #{patid},</if>
smartor/src/main/resources/mapper/smartor/ServiceSubtaskRecordMapper.xml
@@ -4,7 +4,7 @@
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.smartor.mapper.ServiceSubtaskRecordMapper">
    <resultMap type="com.smartor.domain.ServiceSubtaskRecord" id="ServiceSubtaskRecordResult">
    <resultMap type="com.ruoyi.task.domain.ServiceSubtaskRecord" id="ServiceSubtaskRecordResult">
        <result property="id" column="id"/>
        <result property="taskid" column="taskid"/>
        <result property="subtaskId" column="subtask_id"/>
@@ -53,7 +53,7 @@
        from service_subtask_record
    </sql>
    <select id="selectServiceSubtaskRecordList" parameterType="com.smartor.domain.ServiceSubtaskRecord"
    <select id="selectServiceSubtaskRecordList" parameterType="com.ruoyi.task.domain.ServiceSubtaskRecord"
            resultMap="ServiceSubtaskRecordResult">
        <include refid="selectServiceSubtaskRecordVo"/>
        where 1=1
@@ -87,7 +87,7 @@
        where id = #{id}
    </select>
    <insert id="insertServiceSubtaskRecord" parameterType="com.smartor.domain.ServiceSubtaskRecord">
    <insert id="insertServiceSubtaskRecord" parameterType="com.ruoyi.task.domain.ServiceSubtaskRecord">
        insert into service_subtask_record
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">id,</if>
@@ -137,7 +137,7 @@
        </trim>
    </insert>
    <update id="updateServiceSubtaskRecord" parameterType="com.smartor.domain.ServiceSubtaskRecord">
    <update id="updateServiceSubtaskRecord" parameterType="com.ruoyi.task.domain.ServiceSubtaskRecord">
        update service_subtask_record
        <trim prefix="SET" suffixOverrides=",">
            <if test="taskid != null">taskid = #{taskid},</if>