| | |
| | | import com.ruoyi.quartz.enums.MzGatherSlotEnum; |
| | | import com.ruoyi.quartz.enums.MzGatherStatusEnum; |
| | | import com.ruoyi.quartz.service.ICollectHISService; |
| | | import com.ruoyi.quartz.util.CreateDataTable; |
| | | import com.ruoyi.system.domain.SysConfig; |
| | | import com.ruoyi.system.mapper.SysConfigMapper; |
| | | import com.ruoyi.system.service.ISysConfigService; |
| | |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import com.ruoyi.template.domain.HeLibrary; |
| | | import com.ruoyi.inhospvisit.domain.PatMedInhosp; |
| | | import com.ruoyi.template.api.TemplateFacade; |
| | | /** |
| | | * 定时任务调度测试 |
| | | * |
| | |
| | | @Autowired |
| | | private IServiceThirdDataService iServiceThirdDataService; |
| | | |
| | | /** |
| | | * 模板数据一律经 ruoyi-template 的门面取用(跨模块数据访问原则)。 |
| | | */ |
| | | @Autowired |
| | | private TemplateFacade templateFacade; |
| | | |
| | | @Autowired |
| | | private IServiceOutPathService iServiceOutPathService; |
| | | |
| | |
| | | @Autowired |
| | | private IPatMedInhospService iPatMedInhospService; |
| | | |
| | | /** |
| | | * 手术随访模块的<b>常驻门面</b>(原注入 {@code IPatMedOperationService})。 |
| | | * <p> |
| | | * 手术随访编排已整体迁入 {@code ruoyi-othervisit},本类只做调用。 |
| | | * ⚠️ 注入门面而不是模块内部实现:门面永远存在,模块未打进应用或 |
| | | * {@code othervisit.enabled=false} 时只是降级并打 WARN,不会启动失败。 |
| | | */ |
| | | @Autowired |
| | | private IPatMedOuthospService iPatMedOuthospService; |
| | | |
| | | @Autowired |
| | | private IPatMedOperationService iPatMedOperationService; |
| | | private com.ruoyi.othervisit.api.OtherVisitFacade otherVisitFacade; |
| | | |
| | | @Autowired |
| | | private IServiceSubtaskService iServiceSubtaskService; |
| | |
| | | private SysUserImportMapper sysUserImportMapper; |
| | | |
| | | @Autowired |
| | | private HeLibraryMapper heLibraryMapper; |
| | | |
| | | @Autowired |
| | | private ICollectHISService ichService; |
| | | |
| | | @Autowired |
| | |
| | | |
| | | @Autowired |
| | | private ISysUserService userService; |
| | | |
| | | |
| | | @Value("${localIP}") |
| | | private String localIP; |
| | |
| | | IPatMedOuthospService patMedOuthospService; |
| | | |
| | | @Autowired |
| | | ISvyTaskTemplateService svyTaskTemplateService; |
| | | |
| | | @Autowired |
| | | IIvrTaskTemplateService iIvrTaskTemplateService; |
| | | |
| | | @Autowired |
| | | IXHGatherPatArchiveService ixhGatherPatArchiveService; |
| | | |
| | | @Autowired |
| | |
| | | @Autowired |
| | | ICollectHISService icollectHis; |
| | | |
| | | /** |
| | | * 门诊分表维护已整体迁入 {@code ruoyi-outpatientvisit},这里只持有它的常驻门面。 |
| | | * <p> |
| | | * ⚠️ 注入门面而不是内部实现:门面<b>永远存在</b>,即使模块没被打进应用本类也不会启动失败。 |
| | | */ |
| | | @Autowired |
| | | CreateDataTable createDataTable; |
| | | private com.ruoyi.outpatientvisit.api.OuthospVisitFacade outhospVisitFacade; |
| | | |
| | | /** |
| | | * 出入院随访模块的<b>常驻门面</b>。 |
| | | * <p> |
| | | * ⚠️ 2025 修复:本类的 <b>3 个随访入口原先不判开关</b> —— |
| | | * 它们调用 {@code IPatMedInhospService}(smartor 里的遗留实现,见 |
| | | * {@code PatMedInhospServiceImpl}),而那个实现用门面<b>只是为了取数据</b> |
| | | * ({@code selectPatMedInhospList} / {@code updatePatMedInhosp}), |
| | | * <b>从不调用门面的编排方法</b>。于是 {@code inhospvisit.enabled} / |
| | | * {@code publicity.enabled} / {@code othervisit.enabled} 对定时任务<b>完全失效</b>: |
| | | * 门面的编排守卫({@code InhospVisitFacade#dealOutHospInfo} 等)虽然有实现、 |
| | | * 有测试,却只有 HTTP 手动触发那一条路走得到。 |
| | | * <p> |
| | | * 现在改为与手术随访(手术随访早已走 {@code otherVisitFacade},见 {@link #dealOperationInfo()}) |
| | | * 完全同形:定时任务走门面,开关在门面里统一判。模块实现内部会回调 |
| | | * {@code PublicityApi} / {@code OtherVisitApi},那两处各自判自己的开关, |
| | | * 所以一次切换让 3 个开关同时生效。 |
| | | * <p> |
| | | * ⚠️ <b>不能注入 {@code InhospVisitApi} 接口</b>:容器里有两个实现 |
| | | * (本门面 + 内部实现 {@code DefaultInhospVisitApi}),按类型注入会歧义; |
| | | * 而且只有门面才判开关。Quartz 依赖本模块、不存在反向依赖,所以直接注入门面是安全的。 |
| | | */ |
| | | @Autowired |
| | | private com.ruoyi.inhospvisit.api.InhospVisitFacade inhospVisitFacade; |
| | | |
| | | /** |
| | | * 宣教模块的<b>常驻门面</b>,用于判 {@code sys_config: publicity.enabled}。 |
| | | * <p> |
| | | * ⚠️ 2025 修复:本类原本只在**生成**宣教时受开关管(走 inhospvisit 门面回调 PublicityApi), |
| | | * 但**发送**宣教({@link #sfHandlle} 的 {@code type == 3} 分支)从来不读宣教开关 —— |
| | | * 所以关掉 {@code publicity.enabled} 后,已生成的宣教子任务照样通过 |
| | | * 短信/公众号/电话发出去。 |
| | | */ |
| | | @Autowired |
| | | private com.ruoyi.publicity.api.PublicityFacade publicityFacade; |
| | | |
| | | @Value("${appid}") |
| | | private String appid; |
| | |
| | | @Autowired |
| | | private SysConfigMapper sysConfigMapper; |
| | | |
| | | @Autowired |
| | | private IServiceTaskScheduleService serviceTaskScheduleService; |
| | | // ⚠️ 2025 移除:原 `IServiceTaskScheduleService serviceTaskScheduleService` 字段。 |
| | | // 它在本类里**只**被 rollTaskGroupCycle() 用来调 rollCycleTasks()(死字段检查: |
| | | // 改前全文件仅 1 处使用)。该调用已切到 inhospVisitFacade.rollTaskGroupCycle(), |
| | | // 所以字段连带失效并删除 —— 留着它会让人误以为 smartor 的 rollCycleTasks 还有人用。 |
| | | // smartor 侧的 ServiceTaskScheduleServiceImpl.rollCycleTasks() 本身**保留不删** |
| | | // (它可能仍被现网其它入口/接口调用,删除属另一件事)。 |
| | | |
| | | public void ryMultipleParams(String s, Boolean b, Long l, Double d, Integer i) { |
| | | System.out.println(StringUtils.format("执行多参方法: 字符串类型{},布尔类型{},长整型{},浮点型{},整形{}", s, b, l, d, i)); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 定时处理外链的数据 |
| | | */ |
| | |
| | | String formattedNow = now.format(formatter); |
| | | dealHisData(formattedNow); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * HIS数据采集 |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | public void dealHisDataSLTD() { |
| | | log.info("【dealHisData】开始执行HIS数据采集任务"); |
| | | |
| | |
| | | |
| | | /** |
| | | * 处理患者信息,进入子任务表 |
| | | * <p> |
| | | * ⚠️ 2025 修复(开关失效):本方法原走 {@code iPatMedInhospService.dealOutHospInfo(config)}, |
| | | * 那是 smartor 里的遗留实现,**不判任何模块开关** —— 于是: |
| | | * <ul> |
| | | * <li>关 {@code inhospvisit.enabled} 照样生成出院/长期随访;</li> |
| | | * <li>关 {@code publicity.enabled} 照样发入院宣教与药品宣教;</li> |
| | | * <li>关 {@code othervisit.enabled} 照样生成手术随访。</li> |
| | | * </ul> |
| | | * 现改走模块门面:门面判 {@code inhospvisit.enabled},模块实现内部再回调 |
| | | * {@code PublicityApi} / {@code OtherVisitApi},三处各自判自己的开关。 |
| | | */ |
| | | public void dealOutHospInfo() { |
| | | String config = configService.selectConfigByKey("visit.early.day"); |
| | | if (port.equals("8095")) { |
| | | //出院表 |
| | | iPatMedInhospService.dealOutHospInfo(config); |
| | | //出院表(含入院宣教 / 药品宣教 / 手术随访 —— 均由模块实现回调对应模块门面) |
| | | inhospVisitFacade.dealOutHospInfo(config); |
| | | //门诊表(新华的暂时不做门诊随访) |
| | | if (!active.equals("xh")) { |
| | | iPatMedOuthospService.dealOutpatientInfo(); |
| | | // 【门急诊模块化切换】门面内部判 sys_config: outpatientvisit.enabled(运行时热读), |
| | | // 关闭时返回 0 并打 WARN。 |
| | | // |
| | | // ⚠️ 2025 修复:这里原先是 iPatMedOuthospService.dealOutpatientInfo(), |
| | | // 它经 IPatMedOuthospService → PatMedOuthospServiceDelegatingImpl |
| | | // → OuthospDataFacade.dealOutpatientInfo(),而**那条路上没有开关守卫** —— |
| | | // OuthospVisitFacade.dealOutpatientInfo() 才是带守卫的入口,却全仓零调用者。 |
| | | // 结果是 outpatientvisit.enabled=false 对定时任务**完全失效**。 |
| | | // 现在与下面 dealOperationInfo() 走 otherVisitFacade 的形状一致。 |
| | | outhospVisitFacade.dealOutpatientInfo(); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理患者信息,进入子任务表(任务组) |
| | | * <p> |
| | | * ⚠️ 2025 修复(开关失效):原走 smartor 遗留实现,不判开关(同 {@link #dealOutHospInfo()})。 |
| | | * 现走模块门面。注意模块侧的 {@code dealOutHospInfoBySchedule} 内部仍会转发回 smartor |
| | | * ({@code OtherVisitApiBridge},见该类注释),但<b>开关在门面这一层判</b>,所以关闭时不会走到那里。 |
| | | */ |
| | | public void dealOutHospInfoBySchedule() { |
| | | String config = configService.selectConfigByKey("visit.early.day"); |
| | | if (port.equals("8095")) { |
| | | //出院表 |
| | | iPatMedInhospService.dealOutHospInfoBySchedule(config); |
| | | inhospVisitFacade.dealOutHospInfoBySchedule(config); |
| | | //门诊表(新华的暂时不做门诊随访) |
| | | // if (!active.equals("xh")) { |
| | | // //iPatMedOuthospService.dealOutpatientInfo(); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 处理手术信息,进入子任务表 |
| | | */ |
| | | public void dealOperationInfo() { |
| | | if (port.equals("8095")) iPatMedOperationService.dealOperationInfo(); |
| | | // 【手术模块化切换】出口改为调用 ruoyi-othervisit 的常驻门面。 |
| | | // 门面内部判 sys_config: othervisit.enabled(运行时热读),关闭时返回 0 并打 WARN。 |
| | | if (port.equals("8095")) otherVisitFacade.dealOperationInfo(); |
| | | |
| | | } |
| | | |
| | |
| | | */ |
| | | public void createOuthospQuarterTable() { |
| | | log.info("【createOuthospQuarterTable】开始执行门诊分表任务"); |
| | | createDataTable.createOuthospQuarterTable(); |
| | | // 【门诊模块化】CreateDataTable 已整体迁入 ruoyi-outpatientvisit |
| | | // (实现为 OuthospPartitionMaintainer,经 OuthospVisitFacade 暴露)。 |
| | | // 这里只做调用;建表逻辑一行未动。 |
| | | outhospVisitFacade.createOuthospQuarterTable(); |
| | | } |
| | | |
| | | /** |
| | |
| | | List<ServicePatientTemp> servicePatientTemps = iServicePatientTempService.selectServicePatientTempList(new ServicePatientTemp()); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 长期任务发送 |
| | |
| | | if (StringUtils.isNotEmpty(failDay)) iServiceSubtaskService.setFailSubTaskDefalutVal(Long.valueOf(failDay)); |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 判断日期是否是今天 |
| | | * |
| | |
| | | } |
| | | |
| | | String descByCode = ServiceFromEnum.getDescByCode(Integer.valueOf(sendPreachform)); |
| | | |
| | | // ⚠️ 2025 修复(宣教开关失效):type == 3 是**宣教/通知**(纯发送、无作答 —— |
| | | // 见 setSuccessPreachForm 里「宣教或通知…直接改成 9」、以及本方法后面 |
| | | // 「宣教或通知,发送成功则直接改成 6L」的判定),整条派发的所有通道 |
| | | // (电话/短信/公众号/人工)发的都是宣教内容。关掉 publicity.enabled 时应**一个通道都不发**。 |
| | | // |
| | | // ⚠️ 刻意**不推进 subtask 状态、也不记失败**(与 RedisMqReceiver 同口径): |
| | | // 关闭是"暂时不发",不是"这个患者不需要宣教"。标成失败/不需要会让这条宣教 |
| | | // 在开关恢复后永远丢失。保持 sendstate 不动,开关打开后下一次 longTaskSend/compensateTask |
| | | // 会照常把它捞出来发出。 |
| | | if ("3".equals(serviceSubtask.getType()) && !publicityFacade.isEnabled()) { |
| | | log.warn("【宣教】模块未启用(参数配置 publicity.enabled=false),跳过宣教发送。subtaskId={}, patid={}", |
| | | serviceSubtask.getId(), serviceSubtask.getPatid()); |
| | | return; |
| | | } |
| | | |
| | | if (descByCode.equals("电话")) { |
| | | List<String> list = new ArrayList<>(); |
| | | ServiceTask ivrTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | |
| | | sendMagParam.setContent(smsContemt.replace("${wjurl}", sendMagParam.getUrl())); |
| | | } |
| | | } else { |
| | | HeLibrary heLibrary = heLibraryMapper.selectHeLibraryById(serviceSubtask.getLibtemplateid()); |
| | | // ⚠️ 这里**刻意不**为 TemplateModuleDisabledException 做降级: |
| | | // 模板模块关闭(sys_config: template.enabled=false)时,宣教内容确实取不到, |
| | | // 短信也就确实发不出去。外层 try-catch 会如实记为"短信发送失败", |
| | | // 并通过 setSuccessPreachForm(..., "5") 按既有机制升级到下一个通道(公众号/人工)。 |
| | | // 若在这里硬记成"发送成功",等于用一条假记录掩盖"没发出去", |
| | | // 事后从任务记录上完全看不出问题 —— 那是更坏的降级。 |
| | | HeLibrary heLibrary = templateFacade.findHeLibraryById(serviceSubtask.getLibtemplateid()); |
| | | if (heLibrary.getHetype().equals("1")) { |
| | | sendMagParam.setPhone(serviceSubtask.getPhone()); |
| | | url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/xj?p=" + format : localIP + "/xj?p=" + format; |
| | |
| | | SysDept sysDept = CollectionUtils.isNotEmpty(sysDepts) ? sysDepts.get(0) : new SysDept(); |
| | | return ObjectUtils.isNotEmpty(sysDept) ? sysDept.getCampusid() : "2"; |
| | | } |
| | | |
| | | |
| | | private Date getNextVisitTime(Long subid, Long taskid, Date visitTime) { |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform(); |
| | |
| | | } |
| | | try { |
| | | String config = configService.selectConfigByKey("visit.early.day"); |
| | | iPatMedInhospService.dealOutHospInfoBySchedule(config); |
| | | // ⚠️ 2025 修复(开关失效):原走 smartor 遗留实现,不判开关(同 dealOutHospInfo)。 |
| | | // 现走模块门面,与 RyTask#dealOutHospInfo 一致。 |
| | | inhospVisitFacade.dealOutHospInfoBySchedule(config); |
| | | log.info("【dealTaskGroupOutHosp】任务组首轮纳入扫描完成"); |
| | | } catch (Exception e) { |
| | | log.error("【dealTaskGroupOutHosp】任务组扫描异常", e); |
| | |
| | | public void rollTaskGroupCycle() { |
| | | log.info("【rollTaskGroupCycle】开始扫描循环任务组"); |
| | | try { |
| | | int rolled = serviceTaskScheduleService.rollCycleTasks(); |
| | | // ⚠️ 2025 修复(开关失效):原走 smartor 的 ServiceTaskScheduleServiceImpl.rollCycleTasks(), |
| | | // 不判开关 —— 关掉 inhospvisit.enabled 照样「推进轮次 + 新建 subtask」。 |
| | | // |
| | | // 现走模块门面,完成模块化方案里第 10 步一直没做的那一步切换: |
| | | // InhospVisitFacade#rollTaskGroupCycle(判开关) |
| | | // → DefaultInhospVisitApi#rollTaskGroupCycle |
| | | // → TaskGroupCycleRoller#rollCycleTasks(逐行搬自原 L170-190) |
| | | // → ScheduleRollPortBridge(smartor 侧端口实现,116 字段深拷贝等留在实现侧) |
| | | // 该类的注释明确写了「第 10 步切过来之前不能被挂到任何调度上,否则双跑」—— |
| | | // 本行就是那个切换点,切完老的 rollCycleTasks 即退役(全仓只有本方法调它)。 |
| | | int rolled = inhospVisitFacade.rollTaskGroupCycle(); |
| | | // 门面内部的 TaskGroupCycleRoller 已打「本次推进 N 个计划」,这里只补一句总览 |
| | | log.info("【rollTaskGroupCycle】本次推进 {} 个计划", rolled); |
| | | } catch (Exception e) { |
| | | log.error("【rollTaskGroupCycle】滚动调度异常", e); |
| | |
| | | } |
| | | |
| | | /** |
| | | * 处理超时随访任务 |
| | | * @return |
| | | * 处理超时随访任务(随访超时 → 生成"再次随访") |
| | | * <p> |
| | | * ⚠️ 2025 修复(开关失效):本方法原不判任何模块开关 —— |
| | | * 关掉 {@code inhospvisit.enabled} 照样建"再次随访"子任务。 |
| | | * <p> |
| | | * <b>为什么这里是"入口判开关"而不是像 {@link #dealOutHospInfo()} 那样切到门面</b>: |
| | | * 模块侧只提供了 {@code InhospVisitApi#buildAgainVisitDraft}(组装草稿), |
| | | * <b>没有</b>完整的"扫描超时 → 落库 → 回写原任务状态"入口, |
| | | * 而本方法自己组装并 insert(见下方 {@code insertServiceSubtask})。 |
| | | * 造一个模块入口属于新增能力、超出"只解决开关"的范围,所以取最小改动:入口守卫。 |
| | | * <p> |
| | | * ⚠️ 全仓只有 {@code ServiceSubtaskController#dealSubtaskDeadline} 一个调用者。 |
| | | */ |
| | | public void dealSubtaskDeadline(String orgid, Integer deadlineDay){ |
| | | // ⚠️ 开关守卫:先判再干活,与 dealOutHospInfo / rollTaskGroupCycle 同口径。 |
| | | // 关闭时直接返回,不扫描、不建"再次随访"子任务。 |
| | | if (!inhospVisitFacade.isEnabled()) { |
| | | log.warn("【dealSubtaskDeadline】模块未启用(参数配置 inhospvisit.enabled=false),跳过超时随访处理。orgid={}", orgid); |
| | | return; |
| | | } |
| | | //获取任务信息 |
| | | ServiceTask st = new ServiceTask(); |
| | | st.setDelFlag("0"); |