liusheng
9 天以前 34f78281cf395a24e1f3f1c3b861186ff8a03b7b
出入院模块、门急诊模块、模块模块、宣教模块、其它模块
已添加497个文件
74902 ■■■■■ 文件已修改
ruoyi-common/- 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ArchiveReadApi.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/OtherVisitApi.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/OuthospVisitApi.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PreachformApi.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PublicityApi.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PublicityDataPort.java 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PublicitySubtaskApi.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ScheduleRollPort.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ServiceTaskMatchApi.java 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/SubtaskOptionMatchApi.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/SubtaskStatApi.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/TemplateModuleDisabledException.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/VisitTaskCreateApi.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/InHospPublicityRowDto.java 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/OuthospSubtaskDraft.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/PatMedReq.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/PatMedRes.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/PublicityTaskMatchDto.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/ServiceTaskMatchDto.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitAgainSubtaskDto.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitArchiveRow.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitInhospUpdateDto.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitLongTaskDto.java 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitPendingSubtaskDto.java 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitPreachformRow.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitScheduleRollResultDto.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitSubtaskDraft.java 808 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitTaskRuleDto.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/PhoneUtils.java 311 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/pom.xml 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/InhospVisitModule.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultArchiveReadApi.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultInhospVisitApi.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultInhospVisitDataService.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultPreachformApi.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/InhospVisitApi.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/InhospVisitDataService.java 199 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/InhospVisitFacade.java 550 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitAvailabilityInterceptor.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitProperties.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitSwitch.java 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitWebConfig.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/controller/InhospVisitController.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/controller/InhospVisitDataController.java 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/PatMedInhosp.java 589 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/PatMedInhospDTO.java 565 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/PatMedInhospVO.java 461 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitInhospRow.java 572 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitTaskRow.java 444 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitTaskdeptRow.java 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitTaskdiagRow.java 232 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/PatMedInhospMapper.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitArchiveMapper.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitInhospMapper.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitPreachformMapper.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitTaskdeptMapper.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitTaskdiagMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/InhospSubtaskWriter.java 571 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/InhospTaskOrchestrator.java 642 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/InhospVisitTaskService.java 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/TaskGroupCycleRoller.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/VisitPreachformWriter.java 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/support/InhospFlagWriter.java 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/support/VisitSubtaskFactory.java 410 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/PatMedInhospMapper.xml 1181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitArchiveMapper.xml 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitInhospMapper.xml 385 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitPreachformMapper.xml 331 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitTaskdeptMapper.xml 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitTaskdiagMapper.xml 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/test/java/com/ruoyi/inhospvisit/api/InhospVisitContextCheck.java 446 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/test/java/com/ruoyi/inhospvisit/config/InhospVisitSwitchCheck.java 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-inhospvisit/src/test/resources/logback-test.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/pom.xml 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/OtherVisitModule.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/api/OtherVisitApiImpl.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/api/OtherVisitFacade.java 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/config/OtherVisitProperties.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/config/OtherVisitSwitch.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/controller/BaseDictOperationController.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/controller/OperationController.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/controller/OperationItemController.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/domain/BaseDictOperation.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/domain/PatMedOperation.java 551 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/domain/PatMedOperationItem.java 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/mapper/BaseDictOperationMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/mapper/PatMedOperationItemMapper.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/mapper/PatMedOperationMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/BaseDictOperationService.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationItemService.java 150 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationService.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationVisitTaskService.java 367 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/resources/mapper/othervisit/BaseDictOperationMapper.xml 280 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/resources/mapper/othervisit/PatMedOperationItemMapper.xml 792 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-othervisit/src/main/resources/mapper/othervisit/PatMedOperationMapper.xml 940 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/pom.xml 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/OuthospVisitModule.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospDataFacade.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospVisitFacade.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/config/OuthospVisitProperties.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/config/OuthospVisitSwitch.java 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/controller/OuthospController.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/controller/OuthospProvisionalController.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthosp.java 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthospError.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthospProvisional.java 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthospQueryReq.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/mapper/PatMedOuthospErrorMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/mapper/PatMedOuthospMapper.java 131 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/mapper/PatMedOuthospProvisionalMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/service/OuthospDataService.java 492 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/service/OuthospProvisionalService.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/service/OuthospVisitTaskService.java 444 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/support/OuthospPartitionMaintainer.java 665 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/resources/mapper/outpatientvisit/PatMedOuthospErrorMapper.xml 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/resources/mapper/outpatientvisit/PatMedOuthospMapper.xml 536 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-outpatientvisit/src/main/resources/mapper/outpatientvisit/PatMedOuthospProvisionalMapper.xml 455 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/pom.xml 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/PublicityModule.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/api/DefaultPublicityApi.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/api/PublicityFacade.java 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicityAvailabilityInterceptor.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicityProperties.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicitySwitch.java 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicityWebConfig.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/controller/PublicityController.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/service/PublicityService.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/main/java/com/ruoyi/publicity/service/impl/PublicityServiceImpl.java 372 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/test/java/com/ruoyi/publicity/api/PublicityContextCheck.java 212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-publicity/src/test/resources/logback-test.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/pom.xml 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/TemplateModule.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/api/TemplateFacade.java 322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateAvailabilityInterceptor.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateProperties.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateSwitch.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateWebConfig.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/BaseTagController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/BaseTagcategoryController.java 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/HeCategoryController.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLibraryAssortController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLibraryController.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLibraryTagController.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLocallibraryController.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/Icd10AssociationController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaExtemplateCategoryController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaExtemplateController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaExtemplatescriptController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptAssortController.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptController.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptTagController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptTargetController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptTargetoptionController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetAssortController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetController.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetTagController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetoptionController.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateAssortController.java 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateController.java 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateScriptController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateTagController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateTargetoptionController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrTaskTemplateController.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrTaskTemplateScriptController.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrTaskTemplateTargetoptionController.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SchemeLocallibraryController.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyCategoryController.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyFinishController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyFinishoptionController.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyFinishtopicController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibExceptionController.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibResultController.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptCategoryController.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptController.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptOptionController.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptTagController.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateCategoryController.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateController.java 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateScriptWjxController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateTagController.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateTargetoptionController.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateTargetoptionWjxController.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateWjxController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskScriptController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskScriptoptionController.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskTemplateController.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskTemplateScriptController.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskTemplateTargetoptionController.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTopicController.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/controller/TemplateDeptController.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/BaseTag.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/BaseTagcategory.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeCategory.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeCategoryVO.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibrary.java 258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryAssort.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryAssortVO.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryTag.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryVO.java 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLocallibrary.java 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/Icd10Association.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaExtemplate.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaExtemplateCategory.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaExtemplatescript.java 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScript.java 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptAssort.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptAssortVO.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptTag.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptTarget.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptTargetoption.java 234 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptVO.java 357 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTarget.java 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetAssort.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetAssortVO.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetTag.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetTagVO.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetVO.java 231 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetoption.java 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplate.java 377 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateAssort.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateAssortVO.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateScript.java 395 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateScriptVO.java 412 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateTag.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateTargetoption.java 272 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateVO.java 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplatetargetAssort.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplate.java 400 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateScript.java 384 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateScriptVO.java 414 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateTargetoption.java 293 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateVO.java 423 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SchemeLocallibrary.java 437 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyCategory.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyCategoryVO.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyFinish.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyFinishoption.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyFinishtopic.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibException.java 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibResult.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScript.java 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptCategory.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptCategoryVO.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptOption.java 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptRes.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptTag.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplate.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateCategory.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateCategoryVO.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateReq.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateScript.java 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateScriptVO.java 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateScriptWjx.java 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateTag.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateTargetoption.java 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateTargetoptionWjx.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateVO.java 264 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateWjx.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTopicAndOptionRes.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTopicdirection.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskScript.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskScriptoption.java 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplate.java 243 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateScript.java 306 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateScriptVO.java 388 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateTargetoption.java 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateVO.java 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTopic.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTopicReq.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTopicoption.java 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/TemplateDept.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/TemplateDeptVO.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/TemplateSmsRequest.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/domain/VO/HeLibraryCountVO.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/BaseTagMapper.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/BaseTagcategoryMapper.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeCategoryMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLibraryAssortMapper.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLibraryMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLibraryTagMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLocallibraryMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/Icd10AssociationMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaExtemplateCategoryMapper.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaExtemplateMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaExtemplatescriptMapper.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptAssortMapper.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptMapper.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptTagMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptTargetMapper.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptTargetoptionMapper.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetAssortMapper.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetMapper.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetTagMapper.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetoptionMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateAssortMapper.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateScriptMapper.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateTagMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateTargetoptionMapper.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrTaskTemplateMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrTaskTemplateScriptMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrTaskTemplateTargetoptionMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SchemeLocallibraryMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyCategoryMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyFinishMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyFinishoptionMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyFinishtopicMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibExceptionMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibResultMapper.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptCategoryMapper.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptMapper.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptOptionMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptTagMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateCategoryMapper.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateMapper.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateScriptMapper.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateScriptWjxMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateTagMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateTargetoptionMapper.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateTargetoptionWjxMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateWjxMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTopicdirectionMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskScriptMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskScriptoptionMapper.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskTemplateMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskTemplateScriptMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskTemplateTargetoptionMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTopicMapper.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTopicoptionMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/mapper/TemplateDeptMapper.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IBaseTagService.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IBaseTagcategoryService.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IHeCategoryService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLibraryAssortService.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLibraryService.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLibraryTagService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLocallibraryService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIcd10AssociationService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplateCategoryService.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplateService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplatescriptService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptAssortService.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptTagService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptTargetService.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptTargetoptionService.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetAssortService.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetService.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetTagService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetoptionService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateAssortService.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateScriptService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateService.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateTagService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateTargetoptionService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrTaskTemplateScriptService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrTaskTemplateService.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrTaskTemplateTargetoptionService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISchemeLocallibraryService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyCategoryService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyFinishService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyFinishoptionService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyFinishtopicService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibExceptionService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibResultService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptCategoryService.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptOptionService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptService.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptTagService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateCategoryService.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateScriptWjxService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateService.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateTagService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateTargetoptionService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateTargetoptionWjxService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateWjxService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTopicdirectionService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskScriptService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskScriptoptionService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskTemplateScriptService.java 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskTemplateService.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskTemplateTargetoptionService.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTopicService.java 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/ITemplateDeptService.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/BaseTagServiceImpl.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/BaseTagcategoryServiceImpl.java 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeCategoryServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLibraryAssortServiceImpl.java 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLibraryServiceImpl.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLibraryTagServiceImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLocallibraryServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/Icd10AssociationServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplateCategoryServiceImpl.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplateServiceImpl.java 95 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplatescriptServiceImpl.java 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptAssortServiceImpl.java 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptServiceImpl.java 254 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptTagServiceImpl.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptTargetServiceImpl.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptTargetoptionServiceImpl.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetAssortServiceImpl.java 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetServiceImpl.java 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetTagServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetoptionServiceImpl.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateAssortServiceImpl.java 186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateScriptServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateServiceImpl.java 340 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateTagServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateTargetoptionServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrTaskTemplateScriptServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrTaskTemplateServiceImpl.java 337 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrTaskTemplateTargetoptionServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SchemeLocallibraryServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyCategoryServiceImpl.java 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyFinishServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyFinishoptionServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyFinishtopicServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibExceptionServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibResultServiceImpl.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptCategoryServiceImpl.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptOptionServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptServiceImpl.java 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptTagServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateCategoryServiceImpl.java 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateScriptWjxServiceImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateServiceImpl.java 356 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateTagServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateTargetoptionServiceImpl.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateTargetoptionWjxServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateWjxServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTopicdirectionServiceImpl.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskScriptServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskScriptoptionServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskTemplateScriptServiceImpl.java 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskTemplateServiceImpl.java 285 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskTemplateTargetoptionServiceImpl.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTopicServiceImpl.java 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/service/impl/TemplateDeptServiceImpl.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/java/com/ruoyi/template/support/PinYinUtil.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/BaseTagMapper.xml 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/BaseTagcategoryMapper.xml 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/HeCategoryMapper.xml 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/HeLibraryAssortMapper.xml 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/HeLibraryMapper.xml 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/HeLibraryTagMapper.xml 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/HeLocallibraryMapper.xml 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/Icd10AssociationMapper.xml 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaExtemplateCategoryMapper.xml 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaExtemplateMapper.xml 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaExtemplatescriptMapper.xml 182 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptAssortMapper.xml 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptMapper.xml 315 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptTagMapper.xml 141 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptTargetMapper.xml 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptTargetoptionMapper.xml 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetAssortMapper.xml 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetMapper.xml 317 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetTagMapper.xml 146 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetoptionMapper.xml 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateAssortMapper.xml 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateMapper.xml 267 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateScriptMapper.xml 445 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateTagMapper.xml 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateTargetoptionMapper.xml 286 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrTaskTemplateMapper.xml 361 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrTaskTemplateScriptMapper.xml 392 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/IvrTaskTemplateTargetoptionMapper.xml 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SchemeLocallibraryMapper.xml 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyCategoryMapper.xml 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyFinishMapper.xml 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyFinishoptionMapper.xml 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyFinishtopicMapper.xml 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibExceptionMapper.xml 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibResultMapper.xml 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibScriptMapper.xml 246 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibScriptOptionMapper.xml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibScriptTagMapper.xml 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplageCategoryMapper.xml 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateMapper.xml 323 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateScriptMapper.xml 451 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateScriptWjxMapper.xml 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateTagMapper.xml 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateTargetoptionMapper.xml 298 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateTargetoptionWjxMapper.xml 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateWjxMapper.xml 250 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTopicCategoryMapper.xml 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyLibTopicdirectionMapper.xml 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTaskScriptMapper.xml 176 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTaskScriptoptionMapper.xml 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTaskTemplateMapper.xml 279 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTaskTemplateScriptMapper.xml 354 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTaskTemplateTargetoptionMapper.xml 268 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTopicMapper.xml 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/SvyTopicoptionMapper.xml 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/main/resources/mapper/template/TemplateDeptMapper.xml 197 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-template/src/test/java/com/ruoyi/template/config/TemplateSwitchCheck.java 248 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/-
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
The following files have been resolved:
   org.projectlombok:lombok:jar:1.18.34:compile
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ArchiveReadApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.ruoyi.common.core.service;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
/**
 * æ‚£è€…档案({@code pat_archive})<b>只读</b>契约。
 *
 * <h3>为什么需要它</h3>
 * {@code pat_archive} æ˜¯å…¨é™¢å…±äº«è¡¨ï¼Œå¤šä¸ªéšè®¿æ¨¡å—都要读它:
 * <ul>
 *   <li>出入院随访({@code ruoyi-inhospvisit})—— ç»„装出院/入院随访子任务;</li>
 *   <li>门急诊随访({@code ruoyi-outpatientvisit})—— ç»„装门诊随访子任务;</li>
 *   <li>其他随访({@code ruoyi-othervisit})—— ç»„装手术随访子任务。</li>
 * </ul>
 * å„模块都只读随访组装真正用到的那几列({@link VisitArchiveRow} çš„ 10 ä¸ªå­—段)。
 * è‹¥æ¯ä¸ªæ¨¡å—各写一份只读 Mapper,就会出现"同表多份查询、字段一漂移就静默丢字段"的问题。
 *
 * <h3>⚠️ åªè¯»ï¼Œä¸”不含业务判断</h3>
 * ä¸æä¾›å†™å…¥æ–¹æ³• â€”— æ¡£æ¡ˆçš„写属档案域。查询条件与原实现逐字一致:
 * {@code where id = #{patid} and del_flag = 0}
 * ï¼ˆ<b>已删除的档案会被判为"查不到"</b>,调用方据此走"患者基本信息为空"分支)。
 *
 * @see VisitArchiveRow
 */
public interface ArchiveReadApi {
    /**
     * æŒ‰æ‚£è€…档案ID查询。
     * <p>
     * åŽŸå®žçŽ°ï¼š{@code PatArchiveMapper.selectPatArchiveByPatid}({@code id = #{id} and del_flag = 0})。
     *
     * @param patid æ‚£è€…档案ID
     * @return æ¡£æ¡ˆè¡Œï¼›ä¸å­˜åœ¨ï¼ˆæˆ–已删除)返回 {@code null}
     */
    VisitArchiveRow findByPatid(Long patid);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/OtherVisitApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package com.ruoyi.common.core.service;
/**
 * æ‰‹æœ¯éšè®¿ / é—¨è¯Šéšè®¿èƒ½åŠ›çš„é¢„ç•™æŽ¥å£ã€‚
 *
 * <h3>为什么现在就要留</h3>
 * å‡ºå…¥é™¢éšè®¿çš„入口里混着手术随访的调用:
 * <ul>
 *   <li>{@code PatMedInhospServiceImpl#dealOutHospInfo} L449-451 â€”— {@code !active.equals("xh")} æ—¶è°ƒ
 *       {@code addOperationSubTask(config)};</li>
 *   <li>{@code #dealOutHospInfoBySchedule} L559-608 â€”— ä»»åŠ¡ç»„çš„æ‰‹æœ¯åˆ†æ”¯
 *       ï¼ˆæŸ¥ {@code selectOperationItemListMaxLevel}、走 {@code boxedServiceSubtaskForOp})。</li>
 * </ul>
 * è€Œæ‰‹æœ¯éšè®¿æŒ‰ä½ çš„版图属于第二批的 {@code ruoyi-othervisit},本次<b>不搬</b>。
 * ä½† {@code dealOutHospInfo} æœ¬æ¬¡å°±è¦æ¬è¿› {@code ruoyi-inhospvisit},
 * äºŽæ˜¯å¿…须有地方安放这两个调用点 â€”— å°±æ˜¯æœ¬æŽ¥å£ã€‚
 *
 * <h3>本次(第一批)落地程度</h3>
 * <ul>
 *   <li>接口:现在就建(本文件);</li>
 *   <li>实现:<b>过渡实现留在 smartor</b>(包一层现有 {@code PatMedInhospServiceImpl} çš„æ–¹æ³•,
 *       é›¶é€»è¾‘改动);</li>
 *   <li>第二批做 {@code ruoyi-othervisit} æ—¶ï¼ŒæŠŠå®žçŽ°æ–¹æ¢æˆæ–°æ¨¡å—å³å¯ï¼Œ
 *       {@code ruoyi-inhospvisit} ä¸€è¡Œéƒ½ä¸ç”¨æ”¹ã€‚</li>
 * </ul>
 *
 * <h3>调用方务必降级</h3>
 * å–不到实现时按"不做手术随访"处理并打 WARN,不能影响出院/入院随访主流程。
 */
public interface OtherVisitApi {
    /**
     * æ‰‹æœ¯éšè®¿æ‰«æï¼ˆåŽŸ {@code PatMedInhospServiceImpl#addOperationSubTask},迁移前 L1362-1430)。
     * <p>
     * å†…部行为要点(迁移时必须保留):
     * <ul>
     *   <li>非 {@code active == "hzszlyy"} ç«™ç‚¹æ‰è®¾ {@code inhospstate="1"}(迁移前 L1366-1368);</li>
     *   <li>查 {@code selectOperationItemListMaxLevel}({@code opercheck_flag='0'} ä¸” {@code main_flag='1'});</li>
     *   <li>{@code oplevelcode} ä¸ºç©ºåˆ™è·³è¿‡ï¼›</li>
     *   <li>按离院科室 + ç¦»é™¢ç—…区<b>分别</b>查 {@code ServiceTaskoper} å¹¶åˆå¹¶ï¼›</li>
     *   <li>没配任务 â†’ ç½® {@code opercheck_flag="2"} + ã€Œæ‰€æ‚£çš„æ‰‹æœ¯æœªé…ç½®é•¿æœŸä»»åŠ¡;」。</li>
     * </ul>
     *
     * @param config {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return å¤„理的手术记录数
     */
    int addOperationSubTask(String config);
    /**
     * ä»»åŠ¡ç»„ï¼ˆappltype=5)首轮纳入 â€”— <b>本次未搬,仍由 smartor çš„实现承接</b>。
     *
     * <h3>为什么放在这个接口上</h3>
     * åŽŸ {@code dealOutHospInfoBySchedule}(L471-614) ç”±ä¸¤æ®µæž„成:
     * <ul>
     *   <li><b>非手术分支</b>:按维度过滤扫患者 â†’ {@code boxedServiceSubtask}
     *       â†’ {@code scheduleSubtaskBuilder.buildSubtasksForPatient};</li>
     *   <li><b>手术分支</b>:{@code selectOperationItemListMaxLevel}
     *       + {@code boxedServiceSubtaskForOp} â†’ åŒä¸€ builder。</li>
     * </ul>
     * æ‰‹æœ¯åˆ†æ”¯æ­£æ˜¯æœ¬æŽ¥å£çš„域(手术随访),而非手术分支依赖的
     * {@code ScheduleSubtaskBuilder} åŒæ—¶æœåŠ¡"HIS è‡ªåŠ¨çº³å…¥"与"前端手动勾选"两条路径,
     * ä¸å±žæœ¬æ¬¡èŒƒå›´ã€‚所以<b>整个方法本次留在 smartor</b>,
     * ç”±æ–°æ¨¡å—çš„ Controller é€šè¿‡æœ¬æŽ¥å£è½¬å‘ â€”— è¿™æ ·ç«¯ç‚¹èƒ½è¿è¿›æ–°æ¨¡å—(URL ä¸å˜ï¼‰ï¼Œ
     * è€Œå®žçŽ°ä¸å¿…"搬一半留一半"。
     * <p>
     * ç¬¬äºŒæ‰¹åš {@code ruoyi-othervisit} æ—¶ï¼Œè¿žåŒ {@code ScheduleSubtaskBuilder} ä¸€èµ·æ”¶èµ°ã€‚
     *
     * @param config {@code sys_config: visit.early.day}
     * @return åŽŸå®žçŽ°å›ºå®šè¿”å›ž 1
     */
    int dealOutHospInfoBySchedule(String config);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/OuthospVisitApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.ruoyi.common.core.service;
/**
 * é—¨è¯Šéšè®¿èƒ½åŠ›çš„é¢„ç•™æŽ¥å£ï¼ˆç¬¬äºŒæ‰¹ {@code ruoyi-outpatientvisit} çš„占位)。
 *
 * <h3>为什么现在就要留</h3>
 * å‡ºå…¥é™¢éšè®¿çš„ <b>{@code /smartor/patinhosp/dealOutHospInfo} ç«¯ç‚¹</b>里混着一次门诊随访调用
 * ï¼ˆåŽŸ {@code PatMedInhospController#dealOutHospInfo} L263-265):
 * <pre>
 * if (!active.equals("xh")) {
 *     iPatMedOuthospService.dealOutpatientInfo();   // æ–°åŽæš‚时不做门诊随访
 * }
 * </pre>
 * è¯¥ç«¯ç‚¹æœ¬æ¬¡è¦<b>迁进 {@code ruoyi-inhospvisit}</b>(URL ä¸å˜ï¼‰ï¼Œè€Œæ–°æ¨¡å—<b>不能依赖 smartor</b>
 * ï¼ˆå¦åˆ™å¾ªçŽ¯ä¾èµ–ï¼‰ã€‚æ‰€ä»¥æŠŠ"门诊随访"这一能力下沉成本接口,
 * ç”± smartor æä¾›<b>零逻辑改动的过渡实现</b>({@code OuthospVisitApiBridge})。
 * <p>
 * ç­‰ç¬¬äºŒæ‰¹åš {@code ruoyi-outpatientvisit} æ—¶ï¼Œåªéœ€æŠŠå®žçŽ°æ–¹æ¢æˆæ–°æ¨¡å—ï¼Œ
 * {@code ruoyi-inhospvisit} çš„ Controller <b>一行都不用改</b>。
 *
 * <h3>站点差异(原样保留在实现侧)</h3>
 * åŽŸå®žçŽ°åªåœ¨ {@code active != "xh"} æ—¶æ‰è°ƒé—¨è¯Šéšè®¿ã€‚这个判断本次<b>留在 Controller</b>
 * ï¼ˆå› ä¸ºå®ƒä¸ŽåŽŸä»£ç åœ¨åŒä¸€ä¸ª if é‡Œï¼‰ï¼Œå®žçŽ°ä¾§ä¸åšç«™ç‚¹åˆ¤æ–­ã€‚
 *
 * <h3>调用方务必降级</h3>
 * å–不到实现时按"没有门诊随访要做"处理并打 WARN â€”— é—¨è¯Šéšè®¿å¤±è´¥ä¸èƒ½å½±å“å‡ºé™¢éšè®¿ä¸»æµç¨‹ã€‚
 */
public interface OuthospVisitApi {
    /**
     * é—¨è¯Šç—…人随访扫描(原 {@code IPatMedOuthospService#dealOutpatientInfo})。
     *
     * @return å¤„理的门诊记录数
     */
    int dealOutpatientInfo();
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PreachformApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,95 @@
package com.ruoyi.common.core.service;
import com.ruoyi.common.core.service.dto.VisitPreachformRow;
import java.util.List;
/**
 * éšè®¿å­ä»»åŠ¡ã€Œæ‰§è¡Œæ–¹å¼æ˜Žç»†ã€ï¼ˆ{@code service_subtask_preachform})的对外契约。
 *
 * <h3>为什么需要它(跨模块 SQL æ”¶å£ï¼‰</h3>
 * è¿™å¼ è¡¨çš„归属模块是 {@code ruoyi-inhospvisit},但在收口之前:
 * <ul>
 *   <li>smartor é‡Œ<b>另有一整份</b> {@code ServiceSubtaskPreachformMapper}(9 ä¸ªæ–¹æ³•)
 *       ä¸ŽåŒå XML â€”— ä¸Žæœ¬æ¨¡å—çš„ {@code VisitPreachformMapper} æ˜¯<b>同一个表的两套 CRUD</b>;</li>
 *   <li>有 <b>15 ä¸ªæ–‡ä»¶</b>(跨 smartor / ruoyi-admin / ruoyi-quartz)直连那份 Mapper。</li>
 * </ul>
 * è¿™è¿åäº†"各模块独占自己的数据访问"的原则:改任一边的 SQL éƒ½å¯èƒ½ä¸Žå¦ä¸€è¾¹æ¼‚移。
 * æœ¬å¥‘约把"读写这张表"收敛成<b>唯一入口</b>,调用方一律经此访问。
 *
 * <h3>⚠️ æœ¬å¥‘约是纯数据访问,不含任何业务判断</h3>
 * é€æ–¹æ³•与原 {@code ServiceSubtaskPreachformMapper} å¯¹åº”,条件/列/顺序照抄。
 * "什么时候该插一条、该置什么状态"属于调用方的业务,留在调用方。
 *
 * <h3>⚠️ ä¸‰ä¸ª softDelete* æ˜¯"软删"</h3>
 * åŽŸå®žçŽ°é‡Œ {@code deleteServiceSubtaskPreachformById / ByIds / BySubid} çš„语句
 * éƒ½æ˜¯ {@code update ... set del_flag = 1}(其中一个还用 {@code <delete>} æ ‡ç­¾åŒ…着 update)。
 * æœ¬å¥‘约按<b>实际行为</b>命名为 {@code softDelete*},语义不变。
 *
 * @see VisitPreachformRow
 */
public interface PreachformApi {
    /**
     * æŒ‰ä¸»é”®æŸ¥ï¼ˆåŽŸ {@code selectServiceSubtaskPreachformById})。
     *
     * @return è¡Œï¼›ä¸å­˜åœ¨è¿”回 {@code null}
     */
    VisitPreachformRow findById(Long id);
    /**
     * æŒ‰æ¡ä»¶æŸ¥ï¼ˆåŽŸ {@code selectServiceSubtaskPreachformList})。
     * <p>
     * å›ºå®šæ¡ä»¶ {@code del_flag = 0},其余 8 ä¸ªå­—段为可选条件:
     * {@code subid / taskid / currentSendTime / preachform / sendstate / orgid / sort / compensateTime}。
     *
     * @param query æŸ¥è¯¢æ¡ä»¶ï¼ˆéž null å­—段参与过滤)
     * @return è¡Œé›†åˆï¼›æ— åŒ¹é…è¿”回空集合(<b>不返回 null</b>)
     */
    List<VisitPreachformRow> findList(VisitPreachformRow query);
    /**
     * æŒ‰ {@code taskid + orgid} æŸ¥ï¼ˆåŽŸ {@code selectBySubtask})。
     * <p>
     * âš ï¸ åŽŸæ–¹æ³•åæ˜“è¯¯è§£ï¼šå®ƒ<b>不按 subtask ä¸»é”®æŸ¥</b>,而是以
     * {@code del_flag = 0} åŠ å¯é€‰çš„ {@code taskid}、{@code orgid} ä¸ºæ¡ä»¶ã€‚
     */
    List<VisitPreachformRow> findByTaskidAndOrgid(Long taskid, String orgid);
    /**
     * æ–°å¢žï¼ˆåŽŸ {@code insertServiceSubtaskPreachform})。
     * <p>自增主键会回填到 {@code row.id}(对应原 XML çš„ {@code useGeneratedKeys})。
     *
     * @return true = è½åº“成功(原返回 {@code int},此处按"== 1"折算)
     */
    boolean insert(VisitPreachformRow row);
    /**
     * æŒ‰ä¸»é”®æ›´æ–°ï¼ˆåŽŸ {@code updateServiceSubtaskPreachform},WHERE {@code id = #{id}})。
     */
    boolean updateById(VisitPreachformRow row);
    /**
     * æŒ‰æ¡ä»¶æ›´æ–°ï¼ˆåŽŸ {@code updateSSPByCondition})。
     * <p>
     * SET å­å¥ä¸Ž {@link #updateById} ç›¸åŒï¼ŒåŒºåˆ«åªåœ¨ WHERE:
     * æŒ‰ {@code subid / taskid / preachform / remark / orgid} é€é¡¹å¯é€‰ï¼Œä¸”<b>没有 id æ¡ä»¶</b>。
     * ä¸¤å¤„刻意不合并 â€”— åˆå¹¶ä¼šæ”¹å˜"按什么定位行"的语义。
     */
    boolean updateByCondition(VisitPreachformRow row);
    /**
     * æŒ‰ä¸»é”®è½¯åˆ ï¼ˆåŽŸ {@code deleteServiceSubtaskPreachformById})。
     */
    boolean softDeleteById(Long id);
    /**
     * æŒ‰ä¸»é”®é›†åˆè½¯åˆ ï¼ˆåŽŸ {@code deleteServiceSubtaskPreachformByIds})。
     */
    boolean softDeleteByIds(Long[] ids);
    /**
     * æŒ‰ {@code subid} è½¯åˆ ï¼ˆåŽŸ {@code deleteServiceSubtaskPreachformBySubid})。
     */
    boolean softDeleteBySubid(Long subid);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PublicityApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package com.ruoyi.common.core.service;
/**
 * å®£æ•™èƒ½åŠ›çš„å¯¹å¤–æŽ¥å£ï¼ˆå®žçŽ°ç”± {@code ruoyi-publicity} çš„常驻门面 {@code PublicityFacade} æä¾›ï¼‰ã€‚
 *
 * <h3>为什么要单独拆出来</h3>
 * å®£æ•™åŽŸæœ¬æ··åœ¨ {@code PatMedInhospServiceImpl#dealOutHospInfo} é‡Œï¼ˆè¿ç§»å‰ L331-444):
 * <ol>
 *   <li><b>入院宣教</b>({@code in.helibrary.switch == "2"} æ‰æ‰§è¡Œï¼‰ï¼šç§‘室维度 type=4、病区维度 type=5;</li>
 *   <li><b>药品宣教</b>(仅 {@code active == "sltd"}):按 {@code medicalCode} æ‹†ä¸²æŸ¥
 *       {@code service_taskmedical},命中则 type=0。</li>
 * </ol>
 * è¿™ä¸¤å—<b>不是"随访"</b>而是"宣教",所以按你的决定单独拉成 {@code ruoyi-publicity} æ¨¡å—。
 *
 * <h3>边界:宣教模块不建子任务</h3>
 * å®£æ•™æ¨¡å—只负责"<b>决定要不要发、发什么</b>"(读配置、判站点、拼说明),
 * çœŸæ­£çš„子任务落库与派发<b>回调 {@code ruoyi-inhospvisit}</b>
 * ï¼ˆèµ° {@link PublicitySubtaskApi})。这样 {@code boxedServiceSubtask}
 * çš„宣教分支(type=0/4/5)以及"回写 check_flag"的语义全仓只有一份实现,不会出现两份逻辑漂移。
 *
 * <h3>调用方务必降级</h3>
 * å–不到实现时按"没有宣教要做"处理并打 WARN,绝不能让出入院随访主流程抛异常。
 */
public interface PublicityApi {
    /**
     * å…¥é™¢å®£æ•™ï¼šæŒ‰æ‚£è€…所在科室(type=4)与病区(type=5)分别匹配长期宣教任务。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#dealOutHospInfo} L331-409。
     * <p>
     * å†…部行为要点(迁移时必须保留):
     * <ul>
     *   <li>受 {@code sys_config: in.helibrary.switch == "2"} æŽ§åˆ¶ï¼Œä¸æ˜¯ "2" æ—¶<b>整块不执行</b>;</li>
     *   <li>科室维度与病区维度是<b>两段独立的扫描</b>(各自查 {@code deptcheck_flag='0'} /
     *       {@code in_wardcheck_flag='0'}),不是一次查询分两组;</li>
     *   <li>患者档案为空时把标志位置 "2" å¹¶å†™ {@code longTaskReason="患者基本信息为空"};</li>
     *   <li>科室/病区没配长期宣教任务时置 "2" + æ–‡æ¡ˆã€Œè¯¥æ‚£è€…所在科室未配置入院长期宣教任务;」/
     *       ã€Œè¯¥æ‚£è€…所在病区未配置入院长期宣教任务;」;</li>
     *   <li>病区分支在取已有 {@code longTaskReason} æ—¶<b>没有做 null åˆ¤æ–­</b>(迁移前 L401 ç›´æŽ¥æ‹¼æŽ¥ï¼‰
     *       â€”— è¿™æ˜¯åŽŸç¼ºé™·ï¼Œ<b>原样保留</b>。</li>
     * </ul>
     *
     * @param config {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return æäº¤å¤„理的记录数(仅用于日志/返回,迁移前 {@code dealOutHospInfo} å›ºå®šè¿”回 1)
     */
    int dealInHospPublicity(String config);
    /**
     * è¯å“å®£æ•™ï¼ˆçœç«‹åŒå¾·ä¸“属):按患者的 {@code medicalCode} é€ä¸ªè¯å“ç¼–码匹配长期宣教任务。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#dealOutHospInfo} L411-444。
     * <p>
     * å†…部行为要点(迁移时必须保留):
     * <ul>
     *   <li>仅 {@code active == "sltd"} æ—¶æ‰§è¡Œï¼Œå…¶å®ƒç«™ç‚¹ç›´æŽ¥è¿”回 0;</li>
     *   <li>{@code medicalCode} ä¸ºç©ºåˆ™è·³è¿‡è¯¥æ‚£è€…;查不到的药品<b>直接忽略</b>(不需要宣教);</li>
     *   <li>三个数组长度不一致时取最小长度,<b>越界时把 medicalName ç½®ç©ºä¸²</b>并打 WARN
     *       ï¼ˆè¿ç§»å‰ L428-433 çš„防御性编程,原样保留)。</li>
     * </ul>
     *
     * @param config {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return å‘½ä¸­å¹¶å¤„理的药品数
     */
    int dealMedicalPublicity(String config);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PublicityDataPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,190 @@
package com.ruoyi.common.core.service;
import com.ruoyi.common.core.service.dto.InHospPublicityRowDto;
import com.ruoyi.common.core.service.dto.PublicityTaskMatchDto;
import java.util.List;
/**
 * å®£æ•™æ¨¡å—所需的<b>只读/回写数据端口</b>(接口在 ruoyi-common,实现由 smartor æä¾›ï¼‰ã€‚
 *
 * <h3>为什么要有这个端口</h3>
 * å®£æ•™æ¨¡å—({@code ruoyi-publicity})要做两件事:
 * <ol>
 *   <li>查"这个科室/病区/药品有没有配长期宣教任务"({@code service_taskdept} / {@code service_taskmedical});</li>
 *   <li>扫"哪些在院患者还没做入院宣教"({@code pat_med_inhosp} çš„ {@code in_deptcheck_flag} /
 *       {@code in_wardcheck_flag}),查不到患者档案时把标志位置 "2" å¹¶è¿½åŠ åŽŸå› ã€‚</li>
 * </ol>
 * è¿™äº›è¡¨å’Œ SQL å…¨åœ¨ smartor。若让 {@code ruoyi-publicity} ç›´æŽ¥ä¾èµ– smartor,
 * å°±ä¼šå½¢æˆ {@code smartor â†” ruoyi-publicity} å¾ªçŽ¯ä¾èµ–ï¼ˆsmartor è¦ä¾èµ–它来触发宣教)。
 * è‹¥æŠŠ SQL æŠ„一份到新模块,就会出现"同一份口径两处维护"的隐患
 * â€”— è¿™æ­£æ˜¯æ»¡æ„åº¦æ¨¡å—当初极力避免的坑。
 * <p>
 * æ‰€ä»¥ï¼š<b>接口放 ruoyi-common,实现({@code PublicityDataPortBridge})留在 smartor</b>,
 * å®£æ•™æ¨¡å—运行时按类型查找实现。这与
 * {@link VisitTaskCreateApi} / {@link PublicitySubtaskApi} æ˜¯åŒä¸€æ‰‹æ³•。
 *
 * <h3>调用方务必降级</h3>
 * å–不到实现时按"没有宣教要做"处理并打 WARN,绝不能让出入院随访主流程抛异常。
 */
public interface PublicityDataPort {
    /**
     * æ‰«åœ¨é™¢æ‚£è€…(原 {@code patMedInhospMapper.selectPatMedInhospList} çš„两段查询)。
     * <p>
     * è¿ç§»å¯¹åº” {@code PatMedInhospServiceImpl#dealOutHospInfo}:
     * <ul>
     *   <li>科室维度(L325-329):{@code deptcheck_flag="0"}、{@code inhospstate="1"}、{@code fuflag="1"};</li>
     *   <li>病区维度(L376-379):{@code in_wardcheck_flag="0"}、{@code inhospstate="1"}、{@code fuflag="1"}
     *       ï¼ˆå¹¶æŠŠ inDeptcheckFlag / deptcheckFlag ç½® null)。</li>
     * </ul>
     * ä¸¤æ®µæ˜¯<b>独立查询</b>,调用方分两次调(不要合并成一次"OR"查询,那会改变结果集)。
     *
     * @param checkFlagColumn {@code "in_deptcheck_flag"} æˆ– {@code "in_wardcheck_flag"}(只允许这两个值)
     * @return åœ¨é™¢æ‚£è€…行;无数据返回空列表
     */
    List<InHospPublicityRowDto> findInHospForPublicity(String checkFlagColumn);
    /**
     * æŒ‰ç§‘室/病区查长期宣教任务ID(原 {@code serviceTaskdeptMapper.selectServiceTaskdeptList})。
     * <p>
     * è¿ç§»å¯¹åº” L347-353({@code deptType="1"}、{@code serviceType="17"})与
     * L390-396({@code deptType="2"}、{@code serviceType="17"})。
     * SQL æ¡ä»¶ï¼š{@code del_flag = 0}、{@code longtask = 1}、{@code dept_code = ?}、
     * {@code dept_type = ?}、{@code service_type = '17'}、{@code orgid = ?}。
     *
     * @param deptCode  ç§‘室编码(deptType=1)或病区编码(deptType=2)
     * @param deptType  "1" = ç§‘室, "2" = ç—…区
     * @param orgid     æœºæž„ID
     * @param inhospid  è¯¥æ¡è®°å½•对应的出入院记录ID(回填进返回值,方便调用方回调 inhospvisit)
     * @return å‘½ä¸­çš„任务(可能多条);无数据返回空列表
     */
    List<PublicityTaskMatchDto> findDeptPublicityTaskIds(String deptCode, String deptType,
                                                         String orgid, Long inhospid);
    /**
     * æŒ‰è¯å“ç¼–码 + åç§° + å…¬å¸æŸ¥é•¿æœŸå®£æ•™ä»»åŠ¡ID(原 {@code serviceTaskmedicalMapper.selectServiceTaskmedicalList})。
     * <p>
     * è¿ç§»å¯¹åº”原代码 L424-434。原实现设了<b>三个</b>字段:
     * <pre>
     * serviceTaskmedical.setMedicalCode(split[i]);
     * if (i &lt; medicalCompanyArray.length) {
     *     serviceTaskmedical.setMedicalCompany(medicalCompanyArray[i]);
     * } else {
     *     log.warn("...下标越界...");
     *     serviceTaskmedical.setMedicalName("");   // â† æ³¨æ„ï¼šè¶Šç•Œæ—¶è®¾çš„æ˜¯ name,不是 company
     * }
     * </pre>
     * <p>
     * âš ï¸ <b>越界分支是个历史笔误,但会真实影响 SQL,因此必须原样保留</b>:
     * è¶Šç•Œæ—¶ {@code medical_name} è¢«ç½®ä¸º<b>空串</b>(而非 null),
     * è€Œ SQL æ¡ä»¶æ˜¯ {@code <if test="medicalName != null and medicalName != ''">} â€”— ç©ºä¸²ä¼šè·³è¿‡è¯¥æ¡ä»¶ï¼›
     * åŒæ—¶ {@code medical_company} ä¿æŒä¸º <b>null</b>(未被赋值),SQL æ¡ä»¶åŒæ ·è·³è¿‡ã€‚
     * ä¹Ÿå°±æ˜¯è¯´ï¼š<b>越界时两个条件都不过滤</b>(等价于只按 medical_code æŸ¥ï¼‰ã€‚
     * <p>
     * æ‰€ä»¥æœ¬æ–¹æ³•必须把 {@code medicalName} ä¹Ÿä¼ è¿›æ¥ â€”— ä¸èƒ½è®© Bridge è‡ªä½œä¸»å¼ æŠŠç©ºä¸²è½¬æˆ null
     * æˆ–反过来(那会改变 SQL æ¡ä»¶æ˜¯å¦ç”Ÿæ•ˆï¼‰ã€‚
     *
     * @param medicalCode    è¯å“ç¼–码
     * @param medicalName    è¯å“åç§°ï¼›è¶Šç•Œåˆ†æ”¯ä¼ <b>空串</b>;正常分支传 null
     * @param medicalCompany è¯å“å…¬å¸ï¼›è¶Šç•Œåˆ†æ”¯ä¼  null;正常分支传数组里的值
     * @param inhospid       è¯¥æ¡è®°å½•对应的出入院记录ID
     * @return å‘½ä¸­çš„任务(可能多条);无数据返回空列表
     */
    List<PublicityTaskMatchDto> findMedicalPublicityTaskIds(String medicalCode, String medicalName,
                                                            String medicalCompany, Long inhospid);
    /**
     * æ‚£è€…档案是否存在(原 {@code patArchiveMapper.selectPatArchiveByPatid(patid)} çš„判空用途)。
     * <p>
     * è¿ç§»å¯¹åº” L338-344 / L382-388。原实现取的是完整 {@code PatArchive},
     * ä½†å®£æ•™è¿™ä¸€å±‚<b>只用到"是不是 null"</b>(真正的患者信息填充发生在 inhospvisit ç»„子任务时),
     * æ‰€ä»¥è¿™é‡Œåªè¿”回存在性。
     *
     * @param patid æ‚£è€…档案ID(即 {@code PatMedInhosp.patid} / {@code PatArchive.id},都是 Long)
     * @return true = å­˜åœ¨ä¸”未删除
     */
    boolean existsPatArchive(Long patid);
    /**
     * æŠŠå…¥é™¢å®£æ•™çš„æ ‡å¿—位置为给定值,并可同时写 {@code long_task_reason}。
     * <p>
     * è¿ç§»å¯¹åº” L340-342 / L357-359 / L367-370(科室维度)与 L384-386 / L400-402(病区维度)。
     *
     * <h3>⚠️ åŽŸå®žçŽ°æœ‰"覆盖"与"追加"两种写法,必须区分(所以这里给的是 {@code reasonMode})</h3>
     * <table border="1">
     *   <tr><th>场景</th><th>原代码</th><th>语义</th></tr>
     *   <tr><td>患者基本信息为空(科室/病区共用)</td>
     *       <td>{@code setLongTaskReason("患者基本信息为空")}</td>
     *       <td><b>覆盖</b>(直接赋值,丢掉旧值)</td></tr>
     *   <tr><td>科室未配宣教任务</td>
     *       <td>{@code setLongTaskReason(旧值 + "该患者所在科室未配置入院长期宣教任务;")}<br>
     *           å…¶ä¸­"旧值"是<b>按 inhospid é‡æŸ¥ä¸€æ¬¡</b>拿到的</td>
     *       <td><b>追加</b>(且不判旧值是否为 null)</td></tr>
     *   <tr><td>病区未配宣教任务</td>
     *       <td>{@code setLongTaskReason(旧值 + "该患者所在病区未配置入院长期宣教任务;")}</td>
     *       <td><b>追加</b>(同样不判 null;旧值为 null æ—¶ä¼šæ‹¼å‡º {@code "null该患者所在病区..."})</td></tr>
     *   <tr><td>科室处理异常</td>
     *       <td>{@code setLongTaskReason("人员入院科室处理出异常了")}</td>
     *       <td><b>覆盖</b>;且原代码<b>不设 longTaskReason å‰ç¼€</b>、也不重查</td></tr>
     * </table>
     * å› ä¸º"覆盖 / è¿½åŠ "是两种不同语义,本方法用一个布尔区分,而不是让调用方拼字符串
     * â€”— æ‹¼å­—符串会让 {@code null} å‰ç¼€è¿™ç§åŽŸç¼ºé™·è¢«"顺手修掉",那就成了行为变更。
     *
     * @param inhospid   å‡ºå…¥é™¢è®°å½•ID
     * @param flagColumn {@code "in_deptcheck_flag"} æˆ– {@code "in_wardcheck_flag"}(只允许这两个值)
     * @param flagValue  è¦å†™å…¥çš„æ ‡å¿—位值(原实现写 "2")
     * @param reason     è¦å†™å…¥ {@code long_task_reason} çš„æ–‡æ¡ˆï¼›ä¸º null è¡¨ç¤º<b>不动</b>该字段
     * @param appendMode true = è¿½åŠ ï¼ˆ{@code æ—§å€¼ + reason},旧值按 inhospid é‡æŸ¥ï¼Œ<b>不判 null</b>);
     *                   false = è¦†ç›–(直接写 {@code reason})
     * @return true = æ›´æ–°æˆåŠŸ
     */
    boolean updatePublicityFlag(Long inhospid, String flagColumn, String flagValue,
                                String reason, boolean appendMode);
    /**
     * æŒ‰å­—段更新出入院记录(可一次写多个 flag + {@code long_task_reason} + {@code remark})。
     *
     * <h3>为什么要有这个"宽"接口</h3>
     * å‡ºé™¢éšè®¿ç¼–排层({@code InhospTaskOrchestrator} çš„ 3 ä¸ª {@code *ForPatient})在写失败标志位时,
     * åŽŸå®žçŽ°æœ‰ä¸‰ç§ä¸åŒå†™æ³•ï¼Œå†™è¿›åŽ»çš„åˆ—é›†åˆä¹Ÿä¸åŒï¼š
     * <ul>
     *   <li>有的在<b>按 inhospid é‡è¯»å‡ºæ¥çš„对象</b>上改一个 flag,并把另两个 flag
     *       ä»Žé‡è¯»å¯¹è±¡<b>原样带回</b>(因为 {@code updatePatMedInhosp} æ˜¯"传什么更新什么")
     *       â€”— å®žé™…写了 3 ä¸ª flag;</li>
     *   <li>有的在<b>新鲜对象</b>上只 set ä¸€ä¸ª flag â€”— å®žé™…只写 1 ä¸ª flag。</li>
     * </ul>
     * çª„接口 {@link #updatePublicityFlag} è¡¨è¾¾ä¸äº†"一次写多个 flag",
     * æ‰€ä»¥åŠ è¿™ä¸ªé€šé“ã€‚è°ƒç”¨æ–¹æŒ‰åŽŸé€»è¾‘å†³å®šç»™å“ªäº›å­—æ®µèµ‹å€¼ï¼Œ<b>null = ä¸æ”¹</b>
     * ï¼ˆå¯¹åº”原 XML æ¯ä¸ªå­—段各自的 {@code <if test="xxx != null">})。
     *
     * @param update æ›´æ–°è‰ç¨¿ï¼ˆ{@code inhospid} å¿…填)
     * @return true = å½±å“è¡Œæ•° &gt; 0
     */
    boolean updateVisitInhosp(com.ruoyi.common.core.service.dto.VisitInhospUpdateDto update);
    /**
     * æŒ‰ç¦»é™¢ç§‘室/病区查「任务执行规则」(原 {@code ServiceTaskRuleMapper#selectServiceTaskRuleByDeptOrWard})。
     *
     * <h3>为什么放在这个端口上</h3>
     * è¯¥æŸ¥è¯¢æœåŠ¡çš„æ˜¯<b>出院随访</b>的规则编排({@code addSubTaskByRule}),
     * è¯­ä¹‰ä¸Šä¸å±žäºŽ"宣教"。但本端口已经是"随访侧访问 smartor åªè¯»é…ç½®"的统一入口
     * ï¼ˆå®ƒå·²ç»æä¾› {@code findInHospForPublicity} / {@code findDeptPublicityTaskIds} ç­‰ï¼‰ï¼Œ
     * å†ä¸ºä¸€å¼ å•表加第四个端口不划算。这里在注释里写明用途,避免后人误以为它只服务宣教。
     * <p>
     * è¿ç§»å¯¹åº” {@code PatMedInhospServiceImpl#addSubTaskByRule} L1704-1707:
     * <pre>
     * ServiceTaskRule ruleQuery = new ServiceTaskRule();
     * ruleQuery.setDeptcode(patMedInhosp1.getLeaveldeptcode());
     * ruleQuery.setWardcode(patMedInhosp1.getLeavehospitaldistrictcode());
     * ServiceTaskRule rule = serviceTaskRuleMapper.selectServiceTaskRuleByDeptOrWard(ruleQuery);
     * </pre>
     * æ³¨æ„ï¼š<b>该查询最多返回一条</b>(原实现直接取单个对象)。
     *
     * @param leaveldeptcode             ç¦»é™¢ç§‘室编码
     * @param leavehospitaldistrictcode  ç¦»é™¢ç—…区编码
     * @return è§„则;未配置返回 {@code null}
     */
    com.ruoyi.common.core.service.dto.VisitTaskRuleDto findTaskRule(String leaveldeptcode,
                                                                     String leavehospitaldistrictcode);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/PublicitySubtaskApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package com.ruoyi.common.core.service;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—<b>下沉给宣教模块</b>的最小接口:生成一条"宣教类"随访子任务。
 *
 * <h3>为什么需要它(以及为什么放 ruoyi-common)</h3>
 * å®£æ•™ï¼ˆ{@link PublicityApi})与出入院随访是<b>互相调用</b>的:
 * <ul>
 *   <li>{@code ruoyi-inhospvisit} çš„ {@code dealOutHospInfo} è¦è°ƒ {@link PublicityApi} å†³å®š"要不要宣教";</li>
 *   <li>{@code ruoyi-publicity} åˆ¤å®š"要发"之后,又要把建子任务交回 inhospvisit
 *       ï¼ˆè¿™æ · {@code boxedServiceSubtask} çš„宣教分支 type=0/4/5 å…¨ä»“只有一份实现)。</li>
 * </ul>
 * è‹¥ publicity ç›´æŽ¥ä¾èµ– inhospvisit,就形成 {@code inhospvisit <-> publicity} åŒå‘依赖 â†’ Maven æž„建失败。
 * æ‰€ä»¥æŠŠ"宣教模块唯一需要的那一个能力"下沉成本接口放在 ruoyi-common,
 * ä¸¤è¾¹éƒ½åªä¾èµ– ruoyi-common,运行时按类型查找实现(取不到就降级)。
 *
 * <h3>和 ruoyi-inhospvisit é‡Œé‚£ä¸ªåŒåèƒ½åŠ›çš„å…³ç³»</h3>
 * æœ¬æŽ¥å£åªæœ‰ 1 ä¸ªæ–¹æ³•,是给宣教模块用的<b>最小面</b>;
 * {@code com.ruoyi.inhospvisit.api.InhospVisitApi} æ˜¯å®Œæ•´å¥‘约(含出院随访主扫描、任务组轮次滚动等),
 * ä¾›ç›´æŽ¥ä¾èµ–该模块的 smartor / ruoyi-quartz ä½¿ç”¨ã€‚
 * ä¸¤è€…由同一个常驻门面 {@code InhospVisitFacade} ä¸€å¹¶å®žçŽ°ï¼Œå› æ­¤æŒ‰ä»»ä¸€ç§ç±»åž‹æŸ¥æ‰¾éƒ½èƒ½æ‹¿åˆ°åŒä¸€ä¸ª Bean。
 * <p>
 * <b>刻意不做继承</b>:若让完整契约继承本接口,本接口就会"声称"自己也有那些方法,
 * è€ŒæŒ‰æœ¬æŽ¥å£ç¼–译的代码运行期却拿到完整实现 â€”— è¿™æ­£æ˜¯æ»¡æ„åº¦æ¨¡å—"门面把自己当实现"
 * é‚£ç±»å‡è±¡çš„翻版,会掩盖配置错误。
 *
 * @see PublicityApi è°ƒç”¨æ–¹
 */
public interface PublicitySubtaskApi {
    /**
     * ä¸ºæŸä¸ªæ‚£è€…生成一条"宣教类"随访子任务(type=0 è¯å“å®£æ•™ / type=4 å…¥é™¢ç§‘室 / type=5 å…¥é™¢ç—…区)。
     * <p>
     * è¿™æ˜¯ç»™ {@code ruoyi-publicity} ç”¨çš„:宣教模块判定"该发"之后,
     * æŠŠå»ºå­ä»»åŠ¡è¿™ä»¶ï¼ˆå« {@code boxedServiceSubtask} çš„宣教分支、回写 check_flag)交回 inhospvisit。
     *
     * @param taskid   å‘½ä¸­çš„长期宣教任务ID
     * @param patid    æ‚£è€…档案ID
     * @param inhospid å‡ºå…¥é™¢è®°å½•ID
     * @param type     0=药品宣教(出院维度)/ 4=入院科室 / 5=入院病区
     * @param config   {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return true = å·²ç”Ÿæˆï¼ˆæˆ–按原逻辑被标记为不执行)
     */
    boolean generatePublicitySubtask(Long taskid, Long patid, Long inhospid, int type, String config);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ScheduleRollPort.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package com.ruoyi.common.core.service;
import com.ruoyi.common.core.service.dto.VisitScheduleRollResultDto;
import java.util.List;
/**
 * å¾ªçŽ¯ä»»åŠ¡ç»„ã€Œè½®æ¬¡æ»šåŠ¨ã€èƒ½åŠ›çš„é¢„ç•™æŽ¥å£ï¼ˆç¬¬ 9 æ­¥ï¼‰ã€‚
 *
 * <h3>为什么这个接口是"粗粒度"的(重要设计说明)</h3>
 * ä»»åŠ¡ç»„æ»šåŠ¨çš„æ ¸å¿ƒåŠ¨ä½œè¦åšä¸€æ¬¡ <b>{@code ServiceSubtask} çš„全字段深拷贝</b>:
 * <pre>
 * ServiceSubtask copy = JSON.parseObject(JSON.toJSONString(src), ServiceSubtask.class);
 * copy.setId(null); copy.setSenduuid(null); copy.setResult(null); ...
 * </pre>
 * è€Œ {@code ServiceSubtask} æœ‰ <b>116 ä¸ªå­—段</b>。
 * <p>
 * å¦‚果为了"把逻辑搬进新模块"而在模块间传一个 116 å­—段的 DTO,字段映射本身就是巨大风险面,
 * ä¸”<b>换不来任何收益</b> â€”— å…¨å­—段拷贝的语义由 JSON åºåˆ—化保证,不需要逐字段搬运。
 * <p>
 * æ‰€ä»¥æœ¬æŽ¥å£åˆ»æ„åˆ‡æˆä¸¤ä¸ªåŠ¨ä½œï¼ŒæŠŠ"原子性"留在实现侧:
 * <ol>
 *   <li>{@link #selectActiveCycleSchedules(int)} â€”— æŸ¥ä¸€æ‰¹å¾…评估的循环计划;</li>
 *   <li>{@link #rollOneSchedule(Long)} â€”— <b>滚动单个计划(整个动作原子完成,含深拷贝与推进)</b>。</li>
 * </ol>
 * è°ƒç”¨æ–¹ï¼ˆ{@code ruoyi-inhospvisit} çš„ {@code TaskGroupCycleRoller})
 * è´Ÿè´£"批次上限 + é€æ¡ try/catch + è®¡æ•°ä¸Žæ—¥å¿—"这部分<b>编排</b>逻辑。
 * <p>
 * è¿™ä¸Žå…¶å®ƒç«¯å£ï¼ˆ{@code VisitTaskCreateApi} ç­‰ï¼‰çš„粒度不同,是<b>有意的</b>:
 * é‚£äº›ç«¯å£çš„对象字段少、映射可控;这里的对象有 116 ä¸ªå­—段、且语义是"整体克隆"。
 *
 * <h3>与 {@code ServiceTaskScheduleServiceImpl#rollCycleTasks} çš„关系</h3>
 * {@code rollCycleTasks} çš„实现<b>本次刻意保留在 smartor ä¸åЍ</b>(它仍被
 * {@code RyTask.rollTaskGroupCycle} è°ƒç”¨ï¼Œè€Œæ”¹è°ƒç”¨ç‚¹åœ¨ç¬¬ 10 æ­¥ï¼‰ã€‚
 * æœ¬æŽ¥å£çš„实现(过渡 Bridge)与它<b>共享同一套底层能力</b>(同一个 Mapper、同一份深拷贝语义),
 * ä¸å¤åˆ¶ä¸šåŠ¡é€»è¾‘ï¼›ç­‰åˆ°ç¬¬ 10 æ­¥æŠŠè°ƒç”¨ç‚¹åˆ‡è¿‡æ¥åŽï¼Œ{@code rollCycleTasks} æ‰é€€å½¹ã€‚
 *
 * <h3>⚠️ è¿‡æ¸¡æœŸç¦æ­¢åŒè·‘</h3>
 * ç¬¬ 9 æ­¥åšå®ŒåŽï¼Œ<b>「老的 {@code rollCycleTasks}」与「新的 {@code TaskGroupCycleRoller}」
 * ä¸èƒ½åŒæ—¶è¢«è°ƒåº¦</b> â€”— ä¸¤è¾¹éƒ½ä¼š"推进轮次 + æ–°å»º subtask",双跑会造成重复轮次。
 * ç¬¬ 10 æ­¥ä¼šæŠŠ {@code RyTask} çš„æ–¹æ³•体切到新路径;在那之前,新路径只被自检程序调用。
 */
public interface ScheduleRollPort {
    /**
     * æŸ¥ä¸€æ‰¹å¾…评估的循环计划(原 {@code ServiceTaskScheduleMapper#selectActiveCycleSchedules})。
     * <p>
     * è¯¥æŸ¥è¯¢è‡ªèº«çš„æ¡ä»¶ï¼ˆ{@code schedule_style = 1}、{@code max_loop_count} ä¸Šé™ç­‰ï¼‰åœ¨
     * smartor çš„ XML é‡Œï¼Œæœ¬æ¬¡<b>照抄不动</b>,由实现侧保证。
     *
     * @param limit æ‰¹æ¬¡ä¸Šé™ï¼ˆåŽŸå®žçŽ°å›ºå®š 200)
     * @return è®¡åˆ’ID列表;无数据返回空列表
     */
    List<Long> selectActiveCycleSchedules(int limit);
    /**
     * æ»šåŠ¨å•ä¸ªè®¡åˆ’ï¼šè‹¥"本轮已全部结束"则推进到下一轮并克隆 subtask。
     * <p>
     * é€è¡Œå¯¹åº”原 {@code ServiceTaskScheduleServiceImpl#rollOneSchedule}(L196-280),包括:
     * <ul>
     *   <li>上限校验({@code maxLoopCount})与"再开一轮是否超限"的判定;</li>
     *   <li>本轮是否全部结束({@code countActiveByScheduleIdAndLoop});</li>
     *   <li>拉本轮 subtask ä½œå…‹éš†æ¨¡æ¿ + è®¡åˆ’明细按 seq ç´¢å¼•ï¼›</li>
     *   <li>推进步长({@code scheduleTimeNum/Unit})合法性校验;</li>
     *   <li>逐条克隆(<b>全字段深拷贝</b> + æ¸… 6 ä¸ªå­—段 + æŒ‰ detail è¦†ç›–模板 + æŽ¨è¿›æ—¶é—´ï¼‰å¹¶è½åº“ï¼›</li>
     *   <li>复制 {@code service_subtask_preachform} è¡Œï¼›</li>
     *   <li>推进 schedule çš„ {@code current_loop_count / current_seq / next_seq / next_execute_time}。</li>
     * </ul>
     * <b>整个动作必须在实现侧一次性完成</b>(含事务),不能拆成"取模板→改→存"三步跨模块调用。
     *
     * @param scheduleId è®¡åˆ’ID
     * @return æ»šåŠ¨ç»“æžœï¼ˆ{@code rolled=false} æ—¶ {@code skipReason} è¯´æ˜ŽåŽŸå› ï¼‰
     */
    VisitScheduleRollResultDto rollOneSchedule(Long scheduleId);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/ServiceTaskMatchApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,89 @@
package com.ruoyi.common.core.service;
import com.ruoyi.common.core.service.dto.ServiceTaskMatchDto;
import java.util.List;
/**
 * ã€Œé•¿æœŸéšè®¿ä»»åŠ¡åŒ¹é…ã€çš„å¯¹å¤–å¥‘çº¦ã€‚
 *
 * <h3>为什么需要它(本契约是门急诊/手术随访模块化的唯一新增契约)</h3>
 * ä¸‰ä¸ªéšè®¿æ¨¡å—(出入院 {@code ruoyi-inhospvisit}、门急诊 {@code ruoyi-outpatientvisit}、
 * å…¶ä»– {@code ruoyi-othervisit})都要做同一件事:<b>按某个维度查"有没有配长期随访任务"</b>。
 * çŽ°çŠ¶æ˜¯å„è‡ªç›´æŽ¥æ³¨å…¥ smartor çš„ Mapper:
 *
 * <pre>
 * é—¨è¯Šï¼šserviceTaskdeptMapper.selectServiceTaskdeptList(q)   // ç§‘室维度 service_type="3"
 *       serviceTaskdiagMapper.selectServiceTaskdiagList(q)   // è¯Šæ–­ç»´åº¦
 * æ‰‹æœ¯ï¼šserviceTaskoperMapper.selectServiceTaskoperList(q)   // æ‰‹æœ¯ç¼–码维度
 * å®£æ•™ï¼šserviceTaskmedicalMapper...                          // è¯å“ç»´åº¦ï¼ˆæœ¬æ¨¡å—自带 PublicityDataPort)
 * </pre>
 *
 * è¿™äº› Mapper å±žäºŽ smartor çš„「服务任务」域,<b>不能</b>跟着随访模块搬
 * ï¼ˆå®ƒä»¬åŒæ—¶è¢«å‡ºå…¥é™¢/门诊/手术/宣教四方共用,搬给任何一方都会让另外三方反向依赖)。
 * æ‰€ä»¥æŒ‰æ—¢æœ‰å¥—路({@code VisitTaskCreateApi} / {@code PublicityDataPort} åŒæ ·çš„做法)
 * æŠŠ"查"这一层做成 ruoyi-common çš„契约,smartor ä¾§æä¾›è¿‡æ¸¡å®žçŽ°ã€‚
 *
 * <h3>⚠️ æœ¬å¥‘约只管"查",不管"判断"</h3>
 * åŽŸå®žçŽ°é‡Œ"查不到就把标志位置 {@code "2"} å¹¶å†™åŽŸå› æ–‡æ¡ˆ"属于<b>随访域的业务规则</b>,
 * ç•™åœ¨å„随访模块里(门诊/出入院/手术的原因文案各不相同)。
 * æœ¬å¥‘约只做「给定条件 â†’ è¿”回匹配到的行」,与 {@code selectServiceTask*List} é€å­—对应。
 *
 * <h3>⚠️ è¿”回类型不得是 smartor å®žä½“</h3>
 * è§ {@link ServiceTaskMatchDto} çš„反向依赖红线说明。
 *
 * <h3>各方法的原实现对应</h3>
 * <table border="1">
 *   <tr><th>本契约方法</th><th>原 Mapper è°ƒç”¨</th></tr>
 *   <tr><td>{@link #findLongTasksByDept(String, String)}</td>
 *       <td>{@code ServiceTaskdeptMapper.selectServiceTaskdeptList}</td></tr>
 *   <tr><td>{@link #findLongTasksByDiag(String)}</td>
 *       <td>{@code ServiceTaskdiagMapper.selectServiceTaskdiagList}</td></tr>
 *   <tr><td>{@link #findLongTasksByOpcode(String)}</td>
 *       <td>{@code ServiceTaskoperMapper.selectServiceTaskoperList}</td></tr>
 * </table>
 *
 * @see ServiceTaskMatchDto
 */
public interface ServiceTaskMatchApi {
    /**
     * ç§‘室维度:查该科室下已配置的长期随访任务。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆé—¨è¯Š {@code PatMedOuthospServiceImpl#dealOutpatientInfo} L394-398):
     * <pre>
     * ServiceTaskdept q = new ServiceTaskdept();
     * q.setLongtask(1L);
     * q.setDeptCode(patMedOuthosp1.getDeptcode());
     * q.setServiceType("3");
     * List&lt;ServiceTaskdept&gt; list = serviceTaskdeptMapper.selectServiceTaskdeptList(q);
     * </pre>
     * å³ï¼š{@code del_flag=0} + {@code longtask=1} + {@code dept_code=?} + {@code service_type=?}。
     *
     * @param deptCode    ç§‘室编码(原 {@code patMedOuthosp.deptcode})
     * @param serviceType æœåŠ¡ç±»åž‹ï¼ˆé—¨è¯Šä¼  {@code "3"};其它调用方传各自的值,不做假设)
     * @return åŒ¹é…åˆ°çš„任务行;查不到返回空集合(<b>不返回 null</b>)
     */
    List<ServiceTaskMatchDto> findLongTasksByDept(String deptCode, String serviceType);
    /**
     * è¯Šæ–­ç»´åº¦ï¼šæŸ¥è¯¥ ICD10 ä¸‹å·²é…ç½®çš„长期随访任务。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆé—¨è¯Š L410-413):{@code longtask=1} + {@code icd10code=?}(<b>不设 serviceType</b>)。
     *
     * @param icd10code ICD10 ç¼–码(原 {@code patMedOuthosp.icd10code})
     * @return åŒ¹é…åˆ°çš„任务行;查不到返回空集合
     */
    List<ServiceTaskMatchDto> findLongTasksByDiag(String icd10code);
    /**
     * æ‰‹æœ¯ç¼–码维度:查该手术编码下已配置的长期随访任务。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆæ‰‹æœ¯ {@code PatMedOperationServiceImpl#dealOperationInfo} L118-121):
     * {@code longtask=1} + {@code opcode=?}(<b>不设 serviceType</b>)。
     *
     * @param opcode æ‰‹æœ¯ç¼–码(原 {@code patMedOperation.opcode})
     * @return åŒ¹é…åˆ°çš„任务行;查不到返回空集合
     */
    List<ServiceTaskMatchDto> findLongTasksByOpcode(String opcode);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/SubtaskOptionMatchApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.common.core.service;
import java.util.List;
/**
 * ã€ŒAI é€‰é¡¹åŒ¹é…ã€çš„对外契约。
 *
 * <h3>它是什么</h3>
 * åŽŸ {@code IServiceSubtaskService#matchOptionIndex}(smartor):
 * æŠŠã€Œç”¨æˆ·é—®é¢˜ + è¯­éŸ³è¯†åˆ«ç»“æžœ + å€™é€‰é€‰é¡¹ã€åºåˆ—化成 JSON,<b>POST åˆ°ä¸€ä¸ªå¤–部 AI æœåŠ¡</b>,
 * æŠŠè¿”回的数字解析为命中的选项下标(解析失败或 url ä¸ºç©ºè¿”回 {@code -1})。
 *
 * <h3>为什么要下沉成契约</h3>
 * æ¨¡æ¿æ¨¡å—çš„ {@code IvrLibaTargetServiceImpl} éœ€è¦è°ƒå®ƒï¼Œä½†å®ƒæ˜¯ smartor çš„æ–¹æ³•。
 * æ¨¡æ¿æ¨¡å—**绝不能依赖 smartor**(smartor å·²ä¾èµ–模板模块 â†’ ä¼šæˆçŽ¯ï¼ŒMaven ç›´æŽ¥æž„建失败)。
 * æ‰€ä»¥æŒ‰æ—¢æœ‰æ‰‹æ³•({@link VisitTaskCreateApi} / {@link ServiceTaskMatchApi} ç­‰ï¼‰ï¼š
 * æŽ¥å£æ”¾ ruoyi-common,smartor ä¾§æä¾›è¿‡æ¸¡å®žçŽ°ã€‚
 *
 * <h3>⚠️ å…¥å‚为什么用<b>泛型</b> {@code List<T>} è€Œä¸æ˜¯æ¨¡æ¿å®žä½“</h3>
 * åŽŸæ–¹æ³•ç­¾åæ˜¯ {@code List<IvrTaskTemplateTargetoption>},而那个类型已搬进
 * {@code ruoyi-template}。若契约直接写具体类型,ruoyi-common å°±å¾—依赖 ruoyi-template â€”—
 * æ–¹å‘不对。
 * <p>
 * ä¹Ÿä¸èƒ½ç®€å•写成 {@code List<Object>}:调用方拿到返回值后要
 * {@code options.get(matched).getOptiondesc()} â€”— å‚数若是 {@code List<Object>},
 * {@code get()} è¿”回 {@code Object},那行就编译不过。用泛型 {@code <T>} å¯ä»¥è®©
 * è°ƒç”¨ç‚¹çš„类型保持原样,而 ruoyi-common æ— éœ€çŸ¥é“ {@code T} æ˜¯ä»€ä¹ˆã€‚
 * <p>
 * è¯­ä¹‰ä¸Šä¹Ÿå®‰å…¨ï¼šåŽŸæ–¹æ³•å¯¹ options **只做一件事 â€”— {@code jsonObject.put("options", options)}**
 * ç„¶åŽæ•´ä½“序列化,从不读它的字段,所以 {@code T} å…·ä½“是什么都不影响结果。
 *
 * <h3>实现方必须保持的行为</h3>
 * <ul>
 *   <li>{@code url} ä¸ºç©º â†’ ä¸å‘请求,返回 {@code -1};</li>
 *   <li>请求抛异常 â†’ æ‰“日志后返回 {@code -1}(<b>吞异常</b>,原实现如此);</li>
 *   <li>响应非纯整数(允许前导负号)→ è¿”回 {@code -1}。</li>
 * </ul>
 */
public interface SubtaskOptionMatchApi {
    /**
     * è°ƒå¤–部 AI æœåŠ¡åšé€‰é¡¹åŒ¹é…ã€‚
     *
     * @param url          AI æœåŠ¡åœ°å€ï¼ˆåŽŸ {@code ivrTaskTemplate.url});为空则直接返回 -1
     * @param questionText ç”¨æˆ·é—®é¢˜
     * @param voiceText    è¯­éŸ³è¯†åˆ«ç»“æžœ
     * @param options      å€™é€‰é€‰é¡¹ï¼ˆåŽŸä¸º {@code List<IvrTaskTemplateTargetoption>});
     *                     ä»…用于 JSON åºåˆ—化,故用泛型承载
     * @param uuid         æœ¬æ¬¡ä¼šè¯ uuid
     * @param <T>          é€‰é¡¹å…ƒç´ ç±»åž‹ï¼ˆè°ƒç”¨æ–¹å†³å®šï¼Œå®žçŽ°æ–¹ä¸éœ€è¦çŸ¥é“ï¼‰
     * @return å‘½ä¸­çš„下标;无法匹配返回 {@code -1}
     */
    <T> Integer matchOptionIndex(String url, String questionText, String voiceText,
                                 List<T> options, String uuid);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/SubtaskStatApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.common.core.service;
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.core.service.dto.PatMedRes;
/**
 * éšè®¿å­ä»»åŠ¡<b>统计</b>的对外契约(只读)。
 *
 * <h3>为什么单独一个契约,而不是塞进 ServiceTaskMatchApi</h3>
 * {@link ServiceTaskMatchApi} ç®¡çš„æ˜¯"<b>按维度匹配长期随访任务</b>"
 * ï¼ˆç§‘室/诊断/手术编码 â†’ ä»»åŠ¡è¡Œï¼‰ï¼›è€Œæœ¬å¥‘çº¦ç®¡çš„æ˜¯
 * "<b>对 service_subtask åšèšåˆç»Ÿè®¡</b>"。两者虽然都读随访域的数据,
 * ä½†è¯­ä¹‰æ— å…³ï¼Œæ··åœ¨ä¸€ä¸ªå¥‘约里会让调用方难以判断"我到底该依赖谁"。
 *
 * <h3>为什么需要它(跨模块 SQL æ”¶å£ï¼‰</h3>
 * é—¨è¯Šçš„ {@code selectPatMedOuthospCount} éœ€è¦æŠŠ
 * "本域门诊人次/人数" ä¸Ž "随访人次/人数" åˆå¹¶æˆä¸€ä¸ª {@code PatMedRes} è¿”回。
 * åŽè€…查的是 {@code service_subtask}(子任务域),属跨域读;
 * æŒ‰"各模块独占自己的数据访问"原则,应由归属方提供查询能力。
 * åœ¨ã€Œéšè®¿ä»»åŠ¡åˆ›å»ºæ¨¡å—ã€è½åœ°ä¹‹å‰ï¼Œå…ˆæ”¾åœ¨ ruoyi-common。
 *
 * <h3>⚠️ æœ¬å¥‘约只管"查",不管"合并"</h3>
 * åŽŸå®žçŽ°é‡Œ {@code patMedRes.setRc(count)}(把本域算出的门诊人次塞回去)
 * å±žé—¨è¯ŠåŸŸï¼Œç•™åœ¨è°ƒç”¨æ–¹ã€‚
 */
public interface SubtaskStatApi {
    /**
     * é—¨è¯Šéšè®¿äººæ¬¡/人数统计。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆé—¨è¯Š {@code PatMedOuthospServiceImpl#selectPatMedOuthospCount} L345):
     * {@code serviceSubtaskMapper.selectVisitCount(patMedReq)}。
     *
     * <h3>原 SQL å£å¾„(逐字照搬,勿改)</h3>
     * <pre>
     * select 0 AS rc, count(1) AS rs
     * from service_subtask
     * where del_flag = 0 and service_type = 3
     *   [and orgid = ?] [and campusid = ?]
     *   [and date_format(visit_time,'%y%m%d') &gt;= date_format(?,'%y%m%d')]
     *   [and date_format(visit_time,'%y%m%d') &lt;= date_format(?,'%y%m%d')]
     *   [and deptcode in (...)]
     * </pre>
     * æ³¨æ„ï¼šâ‘  å®ƒ<b>刻意把 rc(人次)置 0</b>,人次由调用方另行设置;
     * â‘¡ æ—¥æœŸæ¯”较用 {@code date_format} åˆ°å¤©ï¼Œä¸æ˜¯æ—¶é—´æˆ³æ¯”较。
     *
     * @param req ç»Ÿè®¡æ¡ä»¶ï¼ˆ{@code startDate}/{@code endDate}/{@code orgid}/
     *            {@code campusid}/{@code deptcodeList})
     * @return å« {@code rs}(人数)的结果
     *         âš ï¸ åŽŸå®žçŽ°<b>不判空</b>直接 {@code patMedRes.setRc(...)} â€”—
     *         è‹¥ Mapper è¿”回 null ä¼š NPE。此处保持原行为(不额外兜底),
     *         ç”±è°ƒç”¨æ–¹æŒ‰åŽŸæ ·å¤„ç†ã€‚
     */
    PatMedRes countVisitForStat(PatMedReq req);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/TemplateModuleDisabledException.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package com.ruoyi.common.core.service;
/**
 * æ¨¡æ¿æ¨¡å—被关闭时,访问模板能力的调用会抛出本异常。
 *
 * <h3>为什么需要它(而不是复用 {@code ServiceException})</h3>
 * {@code ServiceException} æ˜¯ <b>final</b> çš„(见 {@code com.ruoyi.common.exception.ServiceException}),
 * æ—¢ä¸èƒ½ç»§æ‰¿ã€ä¹Ÿæ²¡æœ‰å®žçŽ°ä»»ä½•æŽ¥å£ï¼Œå› æ­¤æ— æ³•ç”¨"子类 / æ ‡è®°æŽ¥å£"做到<b>精确捕获</b>。
 * è€Œæœ¬å¼‚常的关键用途恰恰是精确捕获:
 * <pre>
 * try {
 *     HeLibrary lib = templateFacade.findHeLibraryById(id);
 *     ...
 * } catch (TemplateModuleDisabledException e) {
 *     // æ¨¡æ¿è¢«å…³é—­ â€”— åªè·³è¿‡"取模板内容"这一段,随访主流程照常继续
 * }
 * </pre>
 * å¦‚果改用 {@code ServiceException},调用方只能用 {@code catch (ServiceException e)}
 * æˆ–比对 message æ–‡æœ¬ï¼šå‰è€…会把<b>别人的</b>业务异常一起吞掉,
 * åŽè€…会在改文案时静默失效。两者都是隐患,所以这里单独立一个类型。
 *
 * <h3>HTTP è·¯å¾„上的表现</h3>
 * æœ¬å¼‚常是 {@link RuntimeException},会被 RuoYi çš„全局异常处理器
 * {@code GlobalExceptionHandler#handleRuntimeException} æ•获并原样返回
 * {@code e.getMessage()},因此前端拿到的仍是<b>明确的业务提示</b>,不是"系统异常"。
 *
 * <h3>与 {@code TemplateAvailabilityInterceptor} çš„分工</h3>
 * <ul>
 *   <li><b>HTTP ç«¯ç‚¹</b>(66 ä¸ªæ¨¡æ¿ç®¡ç†æŽ¥å£ï¼‰ï¼šç”± {@code TemplateAvailabilityInterceptor}
 *       æ‹¦æˆªï¼ŒæŠ›çš„æ˜¯ {@code ServiceException} â€”— ä¸Žæ»¡æ„åº¦/出入院/宣教模块完全一致,刻意不改;</li>
 *   <li><b>跨模块调用</b>(门面):抛本异常,供调用方精确降级。</li>
 * </ul>
 * ä¸¤è€…<b>文案一致</b>(同一个常量),只是类型不同:一个是给 HTTP è°ƒç”¨æ–¹çœ‹çš„,
 * ä¸€ä¸ªæ˜¯ç»™ Java è°ƒç”¨æ–¹ catch çš„。
 *
 * @see com.ruoyi.template.api.TemplateFacade
 */
public class TemplateModuleDisabledException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    public TemplateModuleDisabledException(String message) {
        super(message);
    }
    public TemplateModuleDisabledException(String message, Throwable cause) {
        super(message, cause);
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/VisitTaskCreateApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,155 @@
package com.ruoyi.common.core.service;
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 java.util.List;
/**
 * éšè®¿ä»»åŠ¡ã€Œåˆ›å»º / æ´¾å‘」能力的预留接口。
 *
 * <h3>为什么单独拆出这个接口</h3>
 * å‡ºå…¥é™¢éšè®¿åŽŸæ¥æŠŠä¸¤ä»¶äº‹æ··åœ¨ä¸€èµ·ï¼š
 * <ol>
 *   <li><b>随访编排</b> â€”— æŒ‰ç§‘室/病区/疾病/手术匹配长期任务、判断再入院该停谁(属 {@code ruoyi-inhospvisit});</li>
 *   <li><b>任务创建与落库</b> â€”— æŸ¥ä»»åŠ¡å®šä¹‰ã€å†™ {@code service_subtask}、终止旧任务(本接口)。</li>
 * </ol>
 * ä½ è¦æ±‚"任务创建将来单独拉一个模块,来调用这些接口",所以这里先把第 2 ä»¶äº‹æŠ½æˆæŽ¥å£ï¼š
 * <ul>
 *   <li>接口定义在 <b>ruoyi-common</b> â€”— è¿™æ˜¯å”¯ä¸€èƒ½åŒæ—¶æ»¡è¶³
 *       ã€Œ{@code ruoyi-inhospvisit} ä¸ä¾èµ– smartor」+「smartor ä¾èµ– {@code ruoyi-inhospvisit}」
 *       +「两者互相要调用」的位置。若把接口放 inhospvisit、实现放 smartor,
 *       å°±ä¼šå½¢æˆ {@code smartor -> inhospvisit -> smartor} çš„循环依赖,Maven ç›´æŽ¥æž„建失败。</li>
 *   <li>本次(第一批)由 <b>smartor æä¾›ä¸€ä¸ªè¿‡æ¸¡å®žçް</b>(逐行搬运现有写库能力,零逻辑改动)。</li>
 *   <li>将来「随访任务创建模块」建好时,<b>只需把该模块的实现注册进来</b>
 *       ï¼ˆå®žçŽ°æ–¹ä»Ž smartor æ¢æˆæ–°æ¨¡å—),{@code ruoyi-inhospvisit} <b>一行都不用改</b>。</li>
 * </ul>
 * è¿™ä¸Žæ»¡æ„åº¦çš„ {@link ISatisfactionCategoryService} æ˜¯åŒä¸€æ‰‹æ³•:接口在 ruoyi-common,
 * è°ƒç”¨æ–¹ç”¨ {@code SpringUtils.getBean(...)} æŒ‰ç±»åž‹æŸ¥æ‰¾ + try/catch é™çº§ã€‚
 *
 * <h3>调用方务必降级</h3>
 * å–不到实现(模块没打进应用 / æœªå¯ç”¨ / å®¹å™¨å¼‚常)时,调用方<b>必须按"不生成子任务"处理并打 WARN</b>,
 * ç»ä¸èƒ½è®©å®šæ—¶ä»»åŠ¡æŠ›å¼‚å¸¸ â€”— å‡ºå…¥é™¢éšè®¿æŒ‚了就是当天全院随访停摆。
 *
 * @see ISatisfactionCategoryService åŒä¸€æ‰‹æ³•的先例
 */
public interface VisitTaskCreateApi {
    /**
     * æŒ‰ taskid å–「已发布的长期任务」(原 {@code ServiceTaskMapper.selectServiceTaskList}
     * é… {@code sendState=2})。取不到时<b>返回 {@code null}</b>。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#writeInSubTask} L686-728(取任务 + ä»»åŠ¡ä¸å­˜åœ¨æ—¶çš„åˆ†æ”¯ï¼‰ã€‚
     *
     * @param taskid ä»»åŠ¡ID
     * @return ä»»åŠ¡å¿«ç…§ï¼›ä¸å­˜åœ¨æˆ–æœªå‘å¸ƒè¿”å›ž {@code null}
     */
    VisitLongTaskDto findPublishedLongTask(Long taskid);
    /**
     * è½åº“一条随访子任务(原 {@code ServiceSubtaskMapper.insertServiceSubtask})。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#writeInSubTask} é‡Œçš„
     * {@code insertServiceSubtask} è°ƒç”¨ï¼ˆL823 / L838 / L854 / L862 / L873)。
     * <p>
     * âš ï¸ å®žçŽ°å¿…é¡»ä¿æŒåŽŸæ ·ï¼šå…¥å‚è‰ç¨¿æ€Žä¹ˆå¡«å°±æ€Žä¹ˆè½åº“ï¼Œ
     * <b>不得在这里"顺手补默认值"或"顺手改状态"</b>,否则线上子任务内容会变。
     * <p>
     * âš ï¸ <b>方法名刻意不叫 createSubtask</b>:它不是"新建一个业务对象",
     * è€Œæ˜¯"把一份已经组装好的草稿原样落库"。
     * å°†æ¥ã€Œéšè®¿ä»»åŠ¡åˆ›å»ºæ¨¡å—ã€æŽ¥ç®¡æ—¶ï¼Œ<b>方法意图必须仍然是"原样落库"</b>,
     * ä¸è®¸åœ¨è¿™ä¸€å±‚加入去重/合并/状态机 â€”— é‚£äº›è¯­ä¹‰åœ¨ {@code ruoyi-inhospvisit} é‡Œã€‚
     *
     * @param draft å­ä»»åŠ¡è‰ç¨¿
     * @return true = è½åº“成功(原实现判断 {@code insertServiceSubtask(...) == 1})
     */
    boolean insertSubtask(VisitSubtaskDraft draft);
    /**
     * æŠŠä¸€æ¡ {@link #findPendingSubtasks} æŸ¥å‡ºæ¥çš„待执行子任务<b>原样回写</b>(整体回写)。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#writeInSubTask} é‡Œçš„
     * {@code serviceSubtaskMapper.updateServiceSubtask(serviceSubtask1)}
     * ï¼ˆL812 / L836)与 {@code #dealInHospInfo} é‡Œçš„ L1349。
     * <p>
     * åŽŸå®žçŽ°çš„åšæ³•æ˜¯ï¼šå…ˆæŸ¥å®Œæ•´å¯¹è±¡ â†’ åœ¨å†…存里改几个字段
     * ï¼ˆ{@code sendstate=4L}、{@code result="error"}、{@code remark=...}、{@code updateBy}、{@code updateTime})
     * â†’ æŠŠ<b>整个对象</b>传给 {@code updateServiceSubtask}。
     * å› ä¸º {@code ServiceSubtaskMapper.xml} çš„ {@code updateServiceSubtask}(L1060-1162)
     * æ˜¯"每个字段各自 {@code <if test="xxx != null">}"、{@code where id = #{id}},
     * æ‰€ä»¥<b>只有非 null å­—段生效</b>。
     * <p>
     * å› æ­¤æœ¬æ–¹æ³•刻意接收<b>同一个 DTO</b>(调用方已经在上面改过要改的字段),
     * ç”±å®žçŽ°æŠŠå®ƒæ˜ å°„å›žå®žä½“åŽæ•´ä½“å›žå†™ â€”— è¿™æ ·"带了哪些字段过去"这件事与原实现逐字段一致,
     * ä¸ä¼šå› ä¸º"只传个 id + å‡ ä¸ªå­—段"而丢掉原实现会带过去的其它字段。
     *
     * @param pending ä»Ž {@link #findPendingSubtasks} æ‹¿åˆ°å¹¶å·²æ”¹å¥½å­—段的 DTO({@code id} å¿…须非空)
     * @return true = æ›´æ–°æˆåŠŸ
     */
    boolean updatePendingSubtask(VisitPendingSubtaskDto pending);
    /**
     * æŸ¥"某患者 + æŸ taskid"下所有 {@code sendstate=2}(待执行)的子任务,用于再入院去重判定。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#writeInSubTask} L760-764 ä¸Ž
     * {@code #dealInHospInfo} L1325-1328 çš„
     * {@code selectServiceSubtaskList(new ServiceSubtaskEntity(patid, sendstate=2, taskid))}。
     *
     * @param patid  æ‚£è€…档案ID
     * @param taskid ä»»åŠ¡ID;{@code dealInHospInfo} é‚£æ¡é“¾è·¯ä¼  {@code null}(按患者全量查)
     * @return å¾…执行子任务摘要列表;无数据返回空列表,<b>不返回 null</b>
     */
    List<VisitPendingSubtaskDto> findPendingSubtasks(Long patid, Long taskid);
    /**
     * å–任务的「执行方式明细」JSON ä¸²ï¼ˆåŽŸ {@code serviceTask.preachformDesc})。
     * <p>
     * è¿ç§»å¯¹åº”:{@code PatMedInhospServiceImpl#addServiceSubtaskPreachform} L1063-1068 â€”—
     * å®ƒå…ˆ {@code selectServiceTaskByTaskid(taskid)} å–到任务,再读 {@code getPreachformDesc()}。
     * <p>
     * <b>为什么不复用 {@link #findPublishedLongTask(Long)}</b>:后者带 {@code sendState=2} æ¡ä»¶
     * ï¼ˆåªè¦"已发布"的任务),而这里原实现用的是 {@code selectServiceTaskByTaskid}
     * ï¼ˆ<b>不带 sendState æ¡ä»¶</b>)—— æ¢æˆå‰è€…会改变"任务查得到/查不到"的判断。
     * æ‰€ä»¥åˆ»æ„åˆ†å¼€æˆä¸¤ä¸ªæ–¹æ³•。
     *
     * @param taskid ä»»åŠ¡ID
     * @return {@code preachformDesc};任务不存在或该字段为空时返回 {@code null}
     */
    String findPreachformDesc(Long taskid);
    /**
     * åˆ¤æ–­æŸæ‚£è€…是否存在「指定状态 + æŒ‡å®šæœåŠ¡ç±»åž‹ã€çš„éšè®¿å­ä»»åŠ¡ã€‚
     * <p>
     * åªè¿”回"有没有",不返回列表 â€”— è°ƒç”¨æ–¹ï¼ˆé—¨è¯Šåˆ—表富化)只用它来置
     * {@code serverState="1"},不需要子任务明细。
     *
     * <h3>原实现对应</h3>
     * é—¨è¯Š {@code PatMedOuthospServiceImpl#selectPatMedOuthospList} L108-117:
     * <pre>
     * ServiceSubtaskEntity v = new ServiceSubtaskEntity();
     * v.setPatid(patMedOuthosp1.getPatid());
     * v.setSendstates(Arrays.asList(1L, 2L, 3L));
     * v.setServiceType("3");
     * List&lt;ServiceSubtask&gt; list = serviceSubtaskMapper.selectServiceSubtaskBySendstate(v);
     * if (!CollectionUtils.isEmpty(list)) patMedOuthosp1.setServerState("1");
     * </pre>
     *
     * <h3>⚠️ ä¸Ž {@link #findPendingSubtasks(Long, Long)} çš„区别(别混用)</h3>
     * <table border="1">
     *   <tr><th></th><th>本方法</th><th>findPendingSubtasks</th></tr>
     *   <tr><td>过滤条件</td><td>{@code sendstate IN (...)} + {@code service_type}</td>
     *       <td>固定 {@code sendstate = 2}</td></tr>
     *   <tr><td>是否按 taskid</td><td><b>否</b>(跨该患者所有任务)</td><td>是(可选)</td></tr>
     *   <tr><td>底层 Mapper æ–¹æ³•</td><td>{@code selectServiceSubtaskBySendstate}</td>
     *       <td>{@code selectServiceSubtaskList}</td></tr>
     * </table>
     * ä¸¤è€…查的是<b>不同口径</b>,不能互相替代。
     *
     * @param patid       æ‚£è€…档案ID
     * @param sendstates  å­ä»»åŠ¡çŠ¶æ€é›†åˆï¼ˆé—¨è¯Šä¼  1/2/3);为空则退化为不按状态过滤
     * @param serviceType æœåŠ¡ç±»åž‹ï¼ˆé—¨è¯Šä¼  {@code "3"});为 null åˆ™ä¸è¿‡æ»¤
     * @return true = è‡³å°‘存在一条
     */
    boolean existsSubtaskWithStates(Long patid, java.util.List<Long> sendstates, String serviceType);
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/InHospPublicityRowDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * å®£æ•™æ¨¡å—需要的「在院患者 + å®£æ•™æ ‡å¿—位」只读行。
 *
 * <h3>来源</h3>
 * è¿ç§»å‰ {@code PatMedInhospServiceImpl#dealOutHospInfo} ç”¨
 * {@code patMedInhospMapper.selectPatMedInhospList(pmks)} å–这批患者,
 * å…¶ä¸­ {@code pmks} åˆ†åˆ«è®¾äº†ï¼š
 * <ul>
 *   <li>科室维度:{@code deptcheck_flag="0"} + {@code inhospstate="1"} + {@code fuflag="1"};</li>
 *   <li>病区维度:{@code in_wardcheck_flag="0"} + {@code inhospstate="1"} + {@code fuflag="1"}
 *       ï¼ˆå¹¶ä¸”把 {@code inDeptcheckFlag}/{@code deptcheckFlag} ç½® null)。</li>
 * </ul>
 *
 * <h3>⚠️ patid æ˜¯ Long</h3>
 * {@code pat_med_inhosp.patid} ä¸Ž {@code PatMedInhosp.patid}({@code PatMedInhosp.java:186})
 * éƒ½æ˜¯ {@code Long},{@code PatArchive.id}({@code PatArchive.java:31})也是 {@code Long}。
 * è¿ç§»å‰ {@code patArchiveMapper.selectPatArchiveByPatid(patMedInhosp1.getPatid())}
 * ä¼ çš„就是这个 Long。
 *
 * @see com.ruoyi.common.core.service.PublicityDataPort#findInHospForPublicity(String)
 */
public class InHospPublicityRowDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** å‡ºå…¥é™¢è®°å½•ID(原 inhospid) */
    private Long inhospid;
    /** æ‚£è€…档案ID(原 patid,Long) */
    private Long patid;
    /** æœºæž„ID(原 orgid,查宣教任务时要用) */
    private String orgid;
    /** å…¥é™¢ç§‘室编码(原 deptcode,科室维度用 type=4 æŸ¥å®£æ•™ä»»åŠ¡ï¼‰ */
    private String deptcode;
    /** å…¥é™¢ç—…区编码(原 hospitaldistrictcode,病区维度用 type=5 æŸ¥å®£æ•™ä»»åŠ¡ï¼‰ */
    private String hospitaldistrictcode;
    /**
     * å½“前记录的 {@code long_task_reason}。
     * <p>
     * ã€Œç§‘室/病区未配置宣教任务」时,原实现的写法是
     * <b>重新按 inhospid æŸ¥ä¸€æ¬¡è®°å½•</b>再把已有 reason æ‹¼ä¸Šæ–°æ–‡æ¡ˆå†™å›ž
     * ï¼ˆL356-359 / L399-402)。搬迁后由 {@link com.ruoyi.common.core.service.PublicityDataPort#appendFlagReason}
     * ä¸€æ¬¡å®Œæˆ"读旧值 + æ‹¼æŽ¥ + å†™å›ž",所以这个字段由<b>端口实现</b>关心,
     * å®£æ•™æ¨¡å—只传"要追加什么"。
     */
    private String longTaskReason;
    /** è¯å“ç¼–码串(逗号分隔,原 medicalCode)—— ä»…药品宣教用 */
    private String medicalCode;
    /** è¯å“åç§°ä¸²ï¼ˆé€—号分隔,原 medicalName)—— ä»…药品宣教用 */
    private String medicalName;
    /** è¯å“å…¬å¸ä¸²ï¼ˆé€—号分隔,原 medicalCompany)—— ä»…药品宣教用 */
    private String medicalCompany;
    public Long getInhospid() {
        return inhospid;
    }
    public void setInhospid(Long inhospid) {
        this.inhospid = inhospid;
    }
    public Long getPatid() {
        return patid;
    }
    public void setPatid(Long patid) {
        this.patid = patid;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getDeptcode() {
        return deptcode;
    }
    public void setDeptcode(String deptcode) {
        this.deptcode = deptcode;
    }
    public String getHospitaldistrictcode() {
        return hospitaldistrictcode;
    }
    public void setHospitaldistrictcode(String hospitaldistrictcode) {
        this.hospitaldistrictcode = hospitaldistrictcode;
    }
    public String getLongTaskReason() {
        return longTaskReason;
    }
    public void setLongTaskReason(String longTaskReason) {
        this.longTaskReason = longTaskReason;
    }
    public String getMedicalCode() {
        return medicalCode;
    }
    public void setMedicalCode(String medicalCode) {
        this.medicalCode = medicalCode;
    }
    public String getMedicalName() {
        return medicalName;
    }
    public void setMedicalName(String medicalName) {
        this.medicalName = medicalName;
    }
    public String getMedicalCompany() {
        return medicalCompany;
    }
    public void setMedicalCompany(String medicalCompany) {
        this.medicalCompany = medicalCompany;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/OuthospSubtaskDraft.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,129 @@
package com.ruoyi.common.core.service.dto;
import java.util.Date;
/**
 * é—¨æ€¥è¯Šéšè®¿ä¸“用草稿({@link VisitSubtaskDraft} çš„æ‰©å±•)。
 *
 * <h3>只放"父类真的没有"的字段</h3>
 * é—¨è¯Šçš„ {@code boxedServiceSubtask}(原 {@code PatMedOuthospServiceImpl} L525-581)
 * å¾€å­ä»»åŠ¡ä¸Šè®¾ 35 ä¸ªå€¼ï¼Œå…¶ä¸­<b>绝大多数父类已有等价字段</b>:
 * <table border="1">
 *   <tr><th>门诊写法</th><th>父类字段</th></tr>
 *   <tr><td>{@code patArchive.name}</td><td>{@code sendname}</td></tr>
 *   <tr><td>{@code patArchive.idcardno}</td><td>{@code sfzh}</td></tr>
 *   <tr><td>{@code patArchive.telcode}(空则 {@code relativetelcode})</td><td>{@code phone}</td></tr>
 *   <tr><td>{@code patArchive.sex} / {@code age}</td><td>{@code sex} / {@code age}</td></tr>
 *   <tr><td>{@code patMedOuthosp.id}</td><td>{@code inhospid}</td></tr>
 * </table>
 * æ‰€ä»¥æœ¬ç±»<b>只</b>补三个父类确实没有的字段,不做重复定义 â€”—
 * é‡å¤å®šä¹‰ä¼šè®©"哪个字段才是真正落库的"变得不清楚。
 *
 * <h3>为什么要单独一个类(而不是往 VisitSubtaskDraft é‡ŒåŠ å­—æ®µï¼‰</h3>
 * {@code insertServiceSubtask} æœ‰ 39 ä¸ªæ¡ä»¶åˆ—({@code <if test="xxx != null">})。
 * ç»™å…±äº«çš„ {@code VisitSubtaskDraft} åŠ å­—æ®µï¼Œå°±å¿…é¡»åŒæ—¶å†³å®š"要不要在
 * {@code VisitTaskCreateApiBridge#toSubtaskEntity} é‡Œè¡¥æ˜ å°„"——
 * è¡¥äº†æ˜ å°„会让<b>出入院那条已验收的写入路径多写列</b>。
 * æ‰€ä»¥æŒ‰"另建门诊专用草稿"处理:{@code VisitSubtaskDraft} ä¸Žå‡ºå…¥é™¢é“¾è·¯<b>一行不动</b>,
 * æ¡¥é‡Œç”¨ {@code instanceof} åˆ†æµæ˜ å°„。
 *
 * @see VisitSubtaskDraft
 * @see com.ruoyi.common.core.service.VisitTaskCreateApi#insertSubtask(VisitSubtaskDraft)
 */
public class OuthospSubtaskDraft extends VisitSubtaskDraft {
    private static final long serialVersionUID = 1L;
    /**
     * é—¨è¯Šå·ï¼ˆåŽŸ {@code patMedOuthosp.outhospno} â†’ è½åº“到 {@code service_subtask.hospno})。
     * <p>
     * âš ï¸ çˆ¶ç±» {@link VisitSubtaskDraft} <b>没有</b> {@code hospno} å­—段
     * ï¼ˆ{@code ServiceSubtask} å®žä½“有,父类漏了),所以这里是必需的补充。
     */
    private String hospno;
    /**
     * é—¨è¯Šå°±è¯Šæ—¥æœŸï¼ˆåŽŸ {@code patMedOuthosp.admitdate})。
     * <p>
     * çˆ¶ç±»æ²¡æœ‰å¯¹åº”字段,但它有两处用途:
     * <ol>
     *   <li>落库到 {@code admindate} åˆ—ï¼›</li>
     *   <li>作为 {@code longSendTime} çš„计算基准
     *       ï¼ˆ{@code addDays(admitdate, sendDay)})。</li>
     * </ol>
     * ç”¨é€” 2 ç”±è°ƒç”¨æ–¹è‡ªå·±ç®—完写进父类的 {@code longSendTime}/{@code visitTime},
     * æœ¬å­—段只为用途 1(落库)。
     */
    private Date admitdate;
    /**
     * é—¨è¯ŠæŒ‡å®šçš„随访时间(原 {@code patMedOuthosp.fudate})。
     * <p>
     * éžç©ºæ—¶ç”±<b>调用方</b>用它覆盖按 {@code sendDay} ç®—出的时间并写进父类的
     * {@code longSendTime}/{@code visitTime} â€”— æœ¬å­—段不落库,只作为"调用方算过它"的留痕载体。
     * <p>
     * âš ï¸ è‹¥æŸå¤©è¦è®©æ¡¥è‡ªå·±å‚与这个覆盖计算,必须同时改
     * {@code toSubtaskEntity} ä¸Žå‡ºå…¥é™¢ä¾§çš„对应用法,不可只改一边。
     */
    private Date fudate;
    /**
     * å®Œæˆæ—¶é—´ï¼ˆåŽŸå®žçŽ°çš„ {@code setFinishtime})。
     * <p>
     * é—¨è¯Š {@code boxedServiceSubtask} é‡Œæ²¡æœ‰è®¾å®ƒï¼›å®žé™…设置它的是<b>停旧子任务</b>那条路径
     * ï¼ˆèµ° {@code VisitPendingSubtaskDto#finishtime})。
     * æœ¬å­—段保留是为了让"草稿也能承载 finishtime"这一能力可用,
     * æ¡¥ä¾§å¯¹åº”映射 {@code e.setFinishtime(...)}。
     */
    private Date finishtime;
    /**
     * å…¥é™¢/门诊标识(原 {@code serviceSubtask.setHospType("1")})。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°æ˜¯<b>硬编码 "1"</b>,并不读 {@code serviceTask.getHospType()};
     * å‡ºå…¥é™¢ä¾§åŒç±»ä½ç½®ç¡¬ç¼–码 "2"、宣教侧 "6"。
     * <p>
     * âš ï¸ æ³¨æ„ {@code VisitLongTaskDto} é‡Œé‚£ä¸ª {@code hosType}(少个 p)是历史笔误,
     * ä¸”全仓无人读取它 â€”— ä¸è¦æŠŠå®ƒä¸Žè¿™é‡Œçš„ {@code hospType} æ··ä¸ºä¸€è°ˆã€‚
     */
    private String hospType;
    public String getHospno() {
        return hospno;
    }
    public void setHospno(String hospno) {
        this.hospno = hospno;
    }
    public Date getAdmitdate() {
        return admitdate;
    }
    public void setAdmitdate(Date admitdate) {
        this.admitdate = admitdate;
    }
    public Date getFudate() {
        return fudate;
    }
    public void setFudate(Date fudate) {
        this.fudate = fudate;
    }
    public Date getFinishtime() {
        return finishtime;
    }
    public void setFinishtime(Date finishtime) {
        this.finishtime = finishtime;
    }
    public String getHospType() {
        return hospType;
    }
    public void setHospType(String hospType) {
        this.hospType = hospType;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/PatMedReq.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package com.ruoyi.common.core.service.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.util.Date;
import java.util.List;
/**
 * ä½é™¢ã€é—¨è¯Šã€å‡ºé™¢ è¯·æ±‚入参
 *
 * @author smartor
 * @date 2023-03-04
 */
@ApiModel(value = "PatMedReq", description = "住院、门诊、出院请求入参对象")
@Data
public class PatMedReq extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * é—¨è¯Š(为空则不查)
     */
    @ApiModelProperty(value = "门诊(为空则不查)")
    private Integer mz;
    /**
     * ä½é™¢(为空则不查)
     */
    @ApiModelProperty(value = "住院(为空则不查)")
    private Integer zy;
    /**
     * å‡ºé™¢(为空则不查)
     */
    @ApiModelProperty(value = "出院(为空则不查)")
    private Integer cy;
    /**
     * å¼€å§‹æ—¶é—´
     */
    @ApiModelProperty(value = "开始时间")
    private Date startDate;
    /**
     * ç»“束时间
     */
    @ApiModelProperty(value = "结束时间")
    private Date endDate;
    /**
     * æ”¯æŒå¤šä¸ªç§‘室
     */
    @ApiModelProperty(value = "科室")
    private List<String> deptcodeList;
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/PatMedRes.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,55 @@
package com.ruoyi.common.core.service.dto;
import com.ruoyi.common.core.domain.BaseEntity;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
 * ä½é™¢ã€é—¨è¯Šã€å‡ºé™¢ è¯·æ±‚入参
 *
 * @author smartor
 * @date 2023-03-04
 */
@ApiModel(value = "PatMedRes", description = "住院、门诊、出院请求返参对象")
@Data
public class PatMedRes {
    private static final long serialVersionUID = 1L;
    /**
     * ç§‘室
     */
    @ApiModelProperty(value = "科室")
    private String deptname;
    /**
     * äººæ¬¡
     */
    @ApiModelProperty(value = "人次")
    private Integer rc;
    /**
     * äººæ•°
     */
    @ApiModelProperty(value = "人数")
    private Integer rs;
    /**
     * é¦–次随访人数
     */
    @ApiModelProperty(value = "首次随访人数")
    private Integer scsf;
    /**
     * å†æ¬¡éšè®¿äººæ•°
     */
    @ApiModelProperty(value = "再次随访人数")
    private Integer zcsf;
    /**
     * ä¸“病随访人数
     */
    @ApiModelProperty(value = "专病随访人数")
    private Integer zbsf;
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/PublicityTaskMatchDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * ä¸€æ¡å‘½ä¸­çš„宣教任务匹配({@link com.ruoyi.common.core.service.PublicityDataPort} çš„返回元素)。
 *
 * <p>只带两个字段,因为宣教模块拿到"哪个患者命中哪个任务"之后,
 * å»ºå­ä»»åŠ¡è¿™ä»¶äº‹<b>整个回调</b> {@code ruoyi-inhospvisit}
 * ï¼ˆèµ° {@link com.ruoyi.common.core.service.PublicitySubtaskApi#generatePublicitySubtask}),
 * ç”±é‚£è¾¹ç»Ÿä¸€å®Œæˆ {@code boxedServiceSubtask} çš„ type=0/4/5 åˆ†æ”¯ä¸Ž check_flag å›žå†™ã€‚
 * è¿™æ · {@code boxedServiceSubtask} å…¨ä»“只有一份实现,不会出现两份逻辑漂移。
 *
 * @see com.ruoyi.common.core.service.PublicityDataPort#findDeptPublicityTaskIds
 * @see com.ruoyi.common.core.service.PublicityDataPort#findMedicalPublicityTaskIds
 */
public class PublicityTaskMatchDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** å‘½ä¸­çš„长期宣教任务ID(原 {@code ServiceTaskdept.taskId} / {@code ServiceTaskmedical.taskId}) */
    private Long taskId;
    /** è¯¥æ¡è®°å½•对应的出入院记录ID(原 {@code PatMedInhosp.inhospid}) */
    private Long inhospid;
    public Long getTaskId() {
        return taskId;
    }
    public void setTaskId(Long taskId) {
        this.taskId = taskId;
    }
    public Long getInhospid() {
        return inhospid;
    }
    public void setInhospid(Long inhospid) {
        this.inhospid = inhospid;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/ServiceTaskMatchDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,108 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * ä¸€æ¡ã€Œå·²å‘布的长期随访任务」的匹配结果。
 *
 * <h3>为什么只有这么几个字段</h3>
 * å®ƒæ›¿ä»£çš„æ˜¯ smartor çš„ {@code ServiceTaskdept} / {@code ServiceTaskdiag} /
 * {@code ServiceTaskoper} / {@code ServiceTaskmedical} å››ä¸ªå®žä½“在"任务匹配"这一用途下的返回。
 * å®žæµ‹è°ƒç”¨æ–¹ï¼ˆé—¨è¯Š / æ‰‹æœ¯éšè®¿çš„编排逻辑)**只读 {@code taskId}**,
 * å…¶ä½™å­—段只是"匹配到了哪一行"的回显,用于日志与排查。
 *
 * <p>因此本 DTO åˆ»æ„<b>不</b>把所有实体的字段都堆进来:那样会产生大量恒为 null çš„字段,
 * è®©åŽæ¥è€…以为"这里的字段都该被填"。将来若某个调用方真的需要额外的匹配列,
 * åº”按需新增明确语义的字段,而不是把实体整个搬过来。
 *
 * <h3>⚠️ åå‘依赖红线</h3>
 * æœ¬ç±»<b>不得</b>引用 {@code com.smartor.*}。契约放在 ruoyi-common çš„全部意义就是
 * è®© ruoyi-outpatientvisit / ruoyi-othervisit åªä¾èµ– common å°±èƒ½å·¥ä½œï¼›
 * ä¸€æ—¦å¼•å…¥ smartor ç±»åž‹ï¼Œå››ä¸ªéšè®¿æ¨¡å—都会反向依赖 smartor,Maven ç›´æŽ¥æž„不成。
 *
 * @see ServiceTaskMatchApi
 */
public class ServiceTaskMatchDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** åŒ¹é…åˆ°çš„任务ID({@code service_task*.task_id})—— è°ƒç”¨æ–¹å”¯ä¸€çœŸæ­£ä½¿ç”¨çš„字段 */
    private Long taskId;
    /**
     * åŒ¹é…è¡Œçš„主键({@code service_task*.id})。
     * <p>仅用于日志/排查,不参与业务判断。
     */
    private Long id;
    /** ä»»åŠ¡åï¼ˆ{@code task_name}),仅用于日志 */
    private String taskName;
    /** æ˜¯å¦é•¿æœŸä»»åŠ¡ï¼ˆ{@code longtask}),原查询固定传 1 */
    private Long longtask;
    /** æœåŠ¡ç±»åž‹ï¼ˆ{@code service_type}),原查询按维度传不同值 */
    private String serviceType;
    /**
     * å®žé™…参与匹配的那个编码值(回显用)。
     * <p>按维度分别是:{@code dept_code} / {@code icd10code} / {@code opcode} /
     * {@code medical_code}。调用方一般不读它,仅便于日志与探针比对。
     */
    private String matchCode;
    public Long getTaskId() {
        return taskId;
    }
    public void setTaskId(Long taskId) {
        this.taskId = taskId;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public Long getLongtask() {
        return longtask;
    }
    public void setLongtask(Long longtask) {
        this.longtask = longtask;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public String getMatchCode() {
        return matchCode;
    }
    public void setMatchCode(String matchCode) {
        this.matchCode = matchCode;
    }
    @Override
    public String toString() {
        return "ServiceTaskMatchDto{taskId=" + taskId + ", id=" + id
                + ", taskName='" + taskName + "', longtask=" + longtask
                + ", serviceType='" + serviceType + "', matchCode='" + matchCode + "'}";
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitAgainSubtaskDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package com.ruoyi.common.core.service.dto;
import com.ruoyi.common.core.service.VisitTaskCreateApi;
import java.io.Serializable;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—对外返回的「再次随访子任务草稿」。
 * <p>
 * ç”¨äºŽ {@code RyTask#dealSubtaskDeadline}(随访超时后生成"再次随访"):
 * åŽŸå®žçŽ°å…ˆ {@code selectPatMedInhospList} æŒ‰ {@code inhospid} åæŸ¥å‡ºå…¥é™¢è®°å½•、
 * å†è°ƒ {@code boxedServiceSubtask(...)} ç»„装子任务,然后由 {@code RyTask} è‡ªå·±è¡¥
 * {@code upid / visitCount / visitType / visitDeptCode} ç­‰å­—段再落库。
 * <p>
 * æ¬è¿åŽå‡ºå…¥é™¢è®°å½•与组装逻辑都在 {@code ruoyi-inhospvisit} é‡Œï¼Œ
 * æ‰€ä»¥æŠŠ"组装结果"以本 DTO è¿”回给调用方,由调用方继续补自己的字段并落库
 * ï¼ˆè½åº“本身走 {@link VisitTaskCreateApi#createSubtask})。
 *
 * @see com.ruoyi.inhospvisit.api.InhospVisitApi#buildAgainVisitDraft(Long, Long, String, Integer)
 */
public class VisitAgainSubtaskDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** ç»„装好的子任务草稿;出入院记录查不到时为 {@code null} */
    private VisitSubtaskDraft draft;
    /**
     * æ˜¯å¦æ‰¾åˆ°äº†å¯¹åº”的出入院记录。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°åœ¨æŸ¥ä¸åˆ°æ—¶æ˜¯ {@code continue} è·³è¿‡è¯¥æ¡å­ä»»åŠ¡
     * ï¼ˆ{@code RyTask.java:2061-2066}),调用方必须保持同样的语义。
     */
    private boolean inhospFound;
    /** æœªæ‰¾åˆ°å‡ºå…¥é™¢è®°å½•的原因(仅用于日志,不落库) */
    private String missReason;
    public VisitSubtaskDraft getDraft() {
        return draft;
    }
    public void setDraft(VisitSubtaskDraft draft) {
        this.draft = draft;
    }
    public boolean isInhospFound() {
        return inhospFound;
    }
    public void setInhospFound(boolean inhospFound) {
        this.inhospFound = inhospFound;
    }
    public String getMissReason() {
        return missReason;
    }
    public void setMissReason(String missReason) {
        this.missReason = missReason;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitArchiveRow.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,195 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
import java.util.Date;
/**
 * æ‚£è€…档案的随访侧只读行({@code pat_archive})。
 *
 * <h3>字段来源(@ b25c3b51)</h3>
 * ç²¾ç¡®ç­‰äºŽ {@code boxedServiceSubtask} / {@code boxedServiceSubtaskForOp} å¯¹
 * {@code patArchive} çš„ getter è®¿é—®é›†åˆï¼ˆ9 ä¸ªï¼‰ï¼š
 * <pre>
 * age  id  idcardno  name  notrequiredFlag  notrequiredreason  relativetelcode  sex  telcode
 * </pre>
 * å…¶ä½™ {@code patArchive} é‡Œçš„字段(病历、诊断、地址…)在随访组装里<b>一个都没用到</b>,
 * æ‰€ä»¥åˆ»æ„ä¸æ¬ â€”— æœ¬è¡ŒåªæœåŠ¡éšè®¿ï¼Œä¸æ˜¯"PatArchive çš„副本"。
 *
 * <h3>⚠️ age çš„取法要小心</h3>
 * smartor çš„ {@code selectPatMedInhospList} é‡Œ {@code age} æ˜¯
 * {@code a.age}(或 {@code CONCAT(a.age, a.age_unit)}),而 {@code PatArchive.age} æ˜¯ {@code Long}。
 * æ¬è¿æ—¶å¿…é¡»<b>逐字照抄原列表达式</b>,否则年龄会从"38"变成"38岁"这类不可见差异。
 * æœ¬è¡ŒæŒ‰"查询里怎么写就怎么映射"处理,并在
 * {@code verify_inhospvisit_sql_expansion.py} é‡Œé€æ¡æ¯”对 SELECT åˆ—清单。
 */
public class VisitArchiveRow implements Serializable {
    private static final long serialVersionUID = 1L;
    /** æ‚£è€…档案ID(原 patArchive.id,Long) */
    private Long id;
    /** å§“名 */
    private String name;
    /** èº«ä»½è¯å· */
    private String idcardno;
    /** æ‰‹æœºå· */
    private String telcode;
    /** äº²å±žæ‰‹æœºå·ï¼ˆtelcode ä¸ºç©ºæ—¶å…œåº•) */
    private String relativetelcode;
    /** æ€§åˆ« */
    private Long sex;
    /** å¹´é¾„ */
    private Long age;
    /**
     * å‡ºç”Ÿæ—¥æœŸã€‚
     * <p>
     * âš ï¸ é—¨è¯Šåˆ—表富化需要它:原实现用 {@code patArchive.getBirthdate()} <b>现算</b>精确年龄
     * ï¼ˆå¹´/月/日三段),而不是直接读 {@code age} åˆ—。出入院侧不用这个字段,
     * ä½†ä¸¤ä¸ªæ¨¡å—共用本行类型,故一并带上 â€”— åªè¯»æŸ¥è¯¢å¤šå–一列,不影响已有分支。
     */
    private Date birthdate;
    /**
     * å¹´é¾„单位("岁"/"月"/"天")。
     * <p>
     * âš ï¸ è¿™ä¸‰ä¸ª {@code age*} å­—段(含 {@link #age2} / {@link #ageUnit2})在原
     * {@code PatArchive} é‡Œæ˜¯<b>富化的中间变量</b>:门诊列表富化调 {@code calculateAge(...)}
     * ç®—出前三段后暂存在档案对象上,紧接着拼成门诊记录的 {@code age} å­—符串。
     * <b>它们不落库、也不参与任何持久化</b>,此处仅为忠实保留原逻辑而存在。
     */
    private String ageUnit;
    /** å¹´é¾„第二段(与 {@link #age} åŒæ®µè¯­ä¹‰ï¼Œè§ {@link #ageUnit} çš„说明) */
    private Long age2;
    /** å¹´é¾„第二段的单位(见 {@link #ageUnit} çš„说明) */
    private String ageUnit2;
    /** å…éšè®¿æ ‡è®°ï¼š"1" = ä¸éœ€è¦éšè®¿ï¼ˆåŽŸ notrequired_flag) */
    private String notrequiredFlag;
    /** å…éšè®¿åŽŸå› ï¼ˆåŽŸ notrequired_reason) */
    private String notrequiredreason;
    /** å»ºæ¡£æ—¶é—´ï¼ˆä»…日志/排查用) */
    private Date createTime;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getIdcardno() {
        return idcardno;
    }
    public void setIdcardno(String idcardno) {
        this.idcardno = idcardno;
    }
    public String getTelcode() {
        return telcode;
    }
    public void setTelcode(String telcode) {
        this.telcode = telcode;
    }
    public String getRelativetelcode() {
        return relativetelcode;
    }
    public void setRelativetelcode(String relativetelcode) {
        this.relativetelcode = relativetelcode;
    }
    public Long getSex() {
        return sex;
    }
    public void setSex(Long sex) {
        this.sex = sex;
    }
    public Long getAge() {
        return age;
    }
    public void setAge(Long age) {
        this.age = age;
    }
    public Date getBirthdate() {
        return birthdate;
    }
    public void setBirthdate(Date birthdate) {
        this.birthdate = birthdate;
    }
    public String getAgeUnit() {
        return ageUnit;
    }
    public void setAgeUnit(String ageUnit) {
        this.ageUnit = ageUnit;
    }
    public Long getAge2() {
        return age2;
    }
    public void setAge2(Long age2) {
        this.age2 = age2;
    }
    public String getAgeUnit2() {
        return ageUnit2;
    }
    public void setAgeUnit2(String ageUnit2) {
        this.ageUnit2 = ageUnit2;
    }
    public String getNotrequiredFlag() {
        return notrequiredFlag;
    }
    public void setNotrequiredFlag(String notrequiredFlag) {
        this.notrequiredFlag = notrequiredFlag;
    }
    public String getNotrequiredreason() {
        return notrequiredreason;
    }
    public void setNotrequiredreason(String notrequiredreason) {
        this.notrequiredreason = notrequiredreason;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitInhospUpdateDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * å‡ºå…¥é™¢è®°å½•的「按字段更新」草稿。
 *
 * <h3>为什么需要它(而不是继续用"单个 flag + reason"的窄接口)</h3>
 * ç¬¬ 8 æ­¥çš„ 3 ä¸ª {@code *ForPatient} æ–¹æ³•在写失败标志位时有<b>细微但真实</b>的差异,
 * å¿…须逐一保真:
 * <table border="1">
 *   <tr><th>方法</th><th>原写法</th><th>实际写入的列</th></tr>
 *   <tr><td>{@code addDeptSubTaskForPatient} æ— æ¨¡æ¿åˆ†æ”¯ï¼ˆL1866-1872)</td>
 *       <td>在<b>重读对象</b>上 {@code setDeptcheckFlag("2")} å¹¶è¦†å†™ reason,
 *           æŠŠå¦ä¸¤ä¸ª flag ä»Žé‡è¯»å¯¹è±¡åŽŸæ ·å¸¦å›ž</td>
 *       <td>deptcheck_flag + wardcheck_flag + diagcheck_flag + long_task_reason</td></tr>
 *   <tr><td>{@code addDeptSubTaskForPatient} catch(L1879-1885)</td>
 *       <td>在<b>新鲜对象 patMedInhosp1</b> ä¸Šç›´æŽ¥ set</td>
 *       <td>deptcheck_flag + long_task_reason</td></tr>
 *   <tr><td>{@code addWardSubTaskForPatient} å¤±è´¥åˆ†æ”¯ï¼ˆL1914-1920)</td>
 *       <td>重读对象 {@code setWardcheckFlag("2")},另两个 flag åŽŸæ ·å¸¦å›ž</td>
 *       <td>wardcheck_flag + deptcheck_flag + diagcheck_flag + long_task_reason</td></tr>
 *   <tr><td>{@code addIcd10SubTaskForPatient} ä¸¤å¤„(L1967-1971 / L1985-1991)</td>
 *       <td>重读对象 {@code setDiagcheckFlag("2")} å¹¶è¦†å†™ reason</td>
 *       <td>diagcheck_flag + long_task_reason</td></tr>
 * </table>
 * çª„接口({@code updateFlags(inhospid, column, value, reason)})表达不了"一次写多个 flag",
 * æ‰€ä»¥åŠ è¿™ä¸ªé€šé“ã€‚<b>只有非 null å­—段会被写</b>(对应原 {@code updatePatMedInhosp}
 * é‡Œæ¯ä¸ªå­—段各自的 {@code <if test="xxx != null">})。
 *
 * <h3>⚠️ ä¸Žæ—§æŽ¥å£çš„关系</h3>
 * è¿™æ˜¯ {@code com.ruoyi.common.core.service.PublicityDataPort#updatePublicityFlag} çš„æ‰©å±•版
 * ï¼ˆåŽè€…只服务宣教,且只碰一个 flag)。两者并存:宣教继续用窄的那个,随访编排用这个。
 */
public class VisitInhospUpdateDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** å‡ºå…¥é™¢è®°å½•ID(必填) */
    private Long inhospid;
    /** ç¦»é™¢ç§‘室标志位;null = ä¸æ”¹ */
    private String deptcheckFlag;
    /** ç¦»é™¢ç—…区标志位;null = ä¸æ”¹ */
    private String wardcheckFlag;
    /** ç–¾ç—…标志位;null = ä¸æ”¹ */
    private String diagcheckFlag;
    /** å…¥é™¢ç§‘室宣教标志位;null = ä¸æ”¹ */
    private String inDeptcheckFlag;
    /** å…¥é™¢ç—…区宣教标志位;null = ä¸æ”¹ */
    private String inWardcheckFlag;
    /** ä»»åŠ¡ç»„æ ‡å¿—ä½ï¼›null = ä¸æ”¹ */
    private String scheduleCheckFlag;
    /** è¯å“å®£æ•™æ ‡å¿—位;null = ä¸æ”¹ */
    private String medcheckFlag;
    /** long_task_reason;null = ä¸æ”¹ï¼ˆåŽŸ XML çš„ <if test="longTaskReason != null">) */
    private String longTaskReason;
    /** remark;null = ä¸æ”¹ */
    private String remark;
    public Long getInhospid() {
        return inhospid;
    }
    public void setInhospid(Long inhospid) {
        this.inhospid = inhospid;
    }
    public String getDeptcheckFlag() {
        return deptcheckFlag;
    }
    public void setDeptcheckFlag(String deptcheckFlag) {
        this.deptcheckFlag = deptcheckFlag;
    }
    public String getWardcheckFlag() {
        return wardcheckFlag;
    }
    public void setWardcheckFlag(String wardcheckFlag) {
        this.wardcheckFlag = wardcheckFlag;
    }
    public String getDiagcheckFlag() {
        return diagcheckFlag;
    }
    public void setDiagcheckFlag(String diagcheckFlag) {
        this.diagcheckFlag = diagcheckFlag;
    }
    public String getInDeptcheckFlag() {
        return inDeptcheckFlag;
    }
    public void setInDeptcheckFlag(String inDeptcheckFlag) {
        this.inDeptcheckFlag = inDeptcheckFlag;
    }
    public String getInWardcheckFlag() {
        return inWardcheckFlag;
    }
    public void setInWardcheckFlag(String inWardcheckFlag) {
        this.inWardcheckFlag = inWardcheckFlag;
    }
    public String getScheduleCheckFlag() {
        return scheduleCheckFlag;
    }
    public void setScheduleCheckFlag(String scheduleCheckFlag) {
        this.scheduleCheckFlag = scheduleCheckFlag;
    }
    public String getMedcheckFlag() {
        return medcheckFlag;
    }
    public void setMedcheckFlag(String medcheckFlag) {
        this.medcheckFlag = medcheckFlag;
    }
    public String getLongTaskReason() {
        return longTaskReason;
    }
    public void setLongTaskReason(String longTaskReason) {
        this.longTaskReason = longTaskReason;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitLongTaskDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,185 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * å·²å‘布的「长期随访任务」快照({@link com.ruoyi.common.core.service.VisitTaskCreateApi} çš„返回值)。
 * <p>
 * æ¥æºï¼šåŽŸ {@code ServiceTaskMapper.selectServiceTaskList(new ServiceTask(taskid, sendState=2))}
 * å–到的第一条记录(见迁移前 {@code PatMedInhospServiceImpl#writeInSubTask} L686-728)。
 * <p>
 * âš ï¸ å­—段是<b>按随访组装实际用到的</b>倒推出来的,不是 {@code ServiceTask} çš„全量 62 ä¸ªå­—段。
 * è¿ç§»æ—¶è‹¥å‘现漏字段,必须补在这里并同步改过渡实现 â€”— æ¼å­—段会导致
 * {@code boxedServiceSubtask} é‡Œå¯¹åº”çš„ setter é™é»˜ä¸ç”Ÿæ•ˆï¼ˆä¸æŠ¥é”™ï¼Œä½†çº¿ä¸Šè¡Œä¸ºå˜äº†ï¼‰ã€‚
 *
 * @see com.ruoyi.common.core.service.VisitTaskCreateApi#findPublishedLongTask(Long)
 */
public class VisitLongTaskDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** ä»»åŠ¡ID */
    private Long taskid;
    /** ä»»åŠ¡åç§° */
    private String taskName;
    /** ä»»åŠ¡ç±»åž‹ï¼ˆ"3" = é€šçŸ¥ç±»ï¼Œå†³å®š visitTime æ˜¯å¦æå‰ N å¤©ï¼Œè§è¿ç§»å‰ L1188-1203) */
    private String type;
    /** æœåŠ¡ç±»åž‹ï¼ˆ"2" å‡ºé™¢éšè®¿ / "13" ä¸“病随访 / "17" å®£æ•™å…³æ€€(入院) / "18" å…¥é™¢éšè®¿ ç­‰ï¼‰ */
    private String serviceType;
    /** é—®å·æ¨¡æ¿ID */
    private Long templateid;
    /** é—®å·æ¨¡æ¿åç§° */
    private String templatename;
    /** è¯­éŸ³æ¨¡æ¿ID(原字段是 String,迁移前用 Long.valueOf() è½¬æ¢ï¼‰ */
    private String libtemplateid;
    /** æŽ¨é€/发送日偏移天数(原 sendDay,缺省按 1 å¤„理) */
    private Long sendDay;
    /** æ‰§è¡Œæ–¹å¼ï¼ˆåŽŸ preachform) */
    private String preachform;
    /** æ‰§è¡Œæ–¹å¼æ˜Žç»† JSON(原 preachformDesc,由 addServiceSubtaskPreachform è§£æžï¼‰ */
    private String preachformDesc;
    /** æœºæž„ID */
    private String orgid;
    /** é™¢åŒºID */
    private String campusid;
    /** å®£æ•™æ¨¡æ¿ï¼ˆåŽŸ preachform çš„同义字段,组装时也在用) */
    private String hosType;
    /** æ›´æ–°äººï¼ˆåŽŸ updateBy,迁移前多处 setUpdateBy(serviceTask.getUpdateBy())) */
    private String updateBy;
    /** åˆ›å»ºäººï¼ˆåŽŸ createBy) */
    private String createBy;
    public Long getTaskid() {
        return taskid;
    }
    public void setTaskid(Long taskid) {
        this.taskid = taskid;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public Long getTemplateid() {
        return templateid;
    }
    public void setTemplateid(Long templateid) {
        this.templateid = templateid;
    }
    public String getTemplatename() {
        return templatename;
    }
    public void setTemplatename(String templatename) {
        this.templatename = templatename;
    }
    public String getLibtemplateid() {
        return libtemplateid;
    }
    public void setLibtemplateid(String libtemplateid) {
        this.libtemplateid = libtemplateid;
    }
    public Long getSendDay() {
        return sendDay;
    }
    public void setSendDay(Long sendDay) {
        this.sendDay = sendDay;
    }
    public String getPreachform() {
        return preachform;
    }
    public void setPreachform(String preachform) {
        this.preachform = preachform;
    }
    public String getPreachformDesc() {
        return preachformDesc;
    }
    public void setPreachformDesc(String preachformDesc) {
        this.preachformDesc = preachformDesc;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getCampusid() {
        return campusid;
    }
    public void setCampusid(String campusid) {
        this.campusid = campusid;
    }
    public String getHosType() {
        return hosType;
    }
    public void setHosType(String hosType) {
        this.hosType = hosType;
    }
    public String getUpdateBy() {
        return updateBy;
    }
    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }
    public String getCreateBy() {
        return createBy;
    }
    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitPendingSubtaskDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,196 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
import java.util.Date;
/**
 * ä¸€æ¡å¾…执行随访子任务的摘要({@link VisitTaskCreateApi#findPendingSubtasks} çš„返回元素)。
 * <p>
 * åªåŒ…含「再入院判定」真正用到的字段(见迁移前
 * {@code PatMedInhospServiceImpl#writeInSubTask} L758-843 ä¸Ž {@code #dealInHospInfo} L1321-1359):
 * <ul>
 *   <li>{@code longSendTime == null} â†’ ä¸æ˜¯é•¿æœŸä»»åŠ¡ï¼Œ<b>直接跳过不处理</b>;</li>
 *   <li>{@code endtime} ç”¨äºŽå’Œ"准备新增的子任务"的出院时间比较,决定停哪一边;</li>
 *   <li>{@code serviceType == "2"} æ˜¯"科室/病区再入院停机"分支的必要条件;</li>
 *   <li>{@code orgid} ç”¨äºŽæ™¯å®ç™½åå• {@code 47255004333112711A1001} åˆ¤æ–­ã€‚</li>
 * </ul>
 *
 * @see com.ruoyi.common.core.service.VisitTaskCreateApi#findPendingSubtasks(Long, Long)
 */
public class VisitPendingSubtaskDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** å­ä»»åŠ¡ID */
    private Long id;
    /**
     * å®Œæˆæ—¶é—´ï¼ˆåŽŸ {@code finishtime})。
     * <p>
     * âš ï¸ <b>为什么需要它</b>:出入院侧的 {@code writeInSubTask} åœæ—§å­ä»»åŠ¡æ—¶
     * <b>不</b>设置 finishtime(基线里那行本来就被注释掉了,原样保留),
     * æ‰€ä»¥å‡ºå…¥é™¢è·¯å¾„从不填它 â€”— è¡Œä¸ºä¸å˜ï¼›
     * ä½†<b>门急诊侧</b>停旧子任务时设了
     * {@code serviceSubtask1.setFinishtime(new Date())}(原
     * {@code PatMedOuthospServiceImpl#writeInSubTask} L477,是活代码),
     * å› æ­¤å¥‘约必须能承载它,否则门诊移植会静默丢掉这一列的写入。
     */
    private Date finishtime;
    /** å‡ºé™¢æ—¶é—´ï¼ˆåŽŸ endtime) */
    private Date endtime;
    /** é•¿æœŸä»»åŠ¡å‘é€æ—¶é—´ï¼›ä¸º null è¡¨ç¤ºä¸æ˜¯é•¿æœŸä»»åŠ¡ */
    private Date longSendTime;
    /** æœåŠ¡ç±»åž‹ï¼ˆ"2" å‡ºé™¢éšè®¿ / "13" ä¸“病随访 â€¦ï¼‰ */
    private String serviceType;
    /** æœºæž„ID(用于景宁白名单判断) */
    private String orgid;
    /** å‘送状态(这里的记录一律是 2 = å¾…发送) */
    private Long sendstate;
    /**
     * ä»»åŠ¡ID。
     * <p>
     * âš ï¸ {@code updateServiceSubtask} çš„ SQL é‡Œæœ‰ä¸€æ¡ {@code <if test="taskid != null">taskid = #{taskid}</if>}
     * â€”— è‹¥å›žå†™æ—¶è¿™ä¸ªå­—段为 null,任务归属就不会被写坏(不改动),
     * ä½†ä¸ºäº†ä¸ŽåŽŸå®žçŽ°<b>逐字段一致</b>(原实现传的是查询出来的完整对象),这里必须带上。
     */
    private Long taskid;
    /**
     * è¦å†™å›žçš„「结果」值。
     * <p>
     * âš ï¸ <b>这是调用方在"移出"一条待执行子任务时改的字段</b>:
     * åŽŸå®žçŽ°æ˜¯æŠŠæŸ¥è¯¢å‡ºæ¥çš„å¯¹è±¡ {@code setResult("error")} åŽå†ä¼ ç»™ update。
     * è¿‡æ¸¡å®žçŽ°ç›´æŽ¥æŠŠå®ƒ {@code setResult(...)} å›žæœ¬ DTO,从而保证
     * "先查完整对象、改几个字段、再整体回写"这条语义<b>逐字段不变</b>。
     */
    private String result;
    /**
     * è¦å†™å›žçš„备注(原实现会把新文案拼在已有 remark åŽé¢ï¼‰ã€‚
     */
    private String remark;
    /** æ›´æ–°äººï¼ˆåŽŸå®žçŽ° {@code setUpdateBy(serviceTask.getUpdateBy())}) */
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ï¼ˆåŽŸå®žçŽ° {@code setUpdateTime(new Date())}) */
    private Date updateTime;
    /**
     * æ˜¯å¦å†æ¬¡éšè®¿ï¼ˆåŽŸ {@code isVisitAgain})。
     * <p>
     * âš ï¸ è¿™æ˜¯ {@code dealInHospInfo}「再入院停机」要写的字段(原 L1346 {@code setIsVisitAgain(2)})。
     * ä¸åŠ å®ƒï¼Œåœæœºæ—¶è¿™ä¸ªæ ‡è®°å°±ä¼šä¸¢ã€‚
     */
    private Integer isVisitAgain;
    public Date getFinishtime() {
        return finishtime;
    }
    public void setFinishtime(Date finishtime) {
        this.finishtime = finishtime;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Date getEndtime() {
        return endtime;
    }
    public void setEndtime(Date endtime) {
        this.endtime = endtime;
    }
    public Date getLongSendTime() {
        return longSendTime;
    }
    public void setLongSendTime(Date longSendTime) {
        this.longSendTime = longSendTime;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public Long getSendstate() {
        return sendstate;
    }
    public void setSendstate(Long sendstate) {
        this.sendstate = sendstate;
    }
    public Long getTaskid() {
        return taskid;
    }
    public void setTaskid(Long taskid) {
        this.taskid = taskid;
    }
    public String getResult() {
        return result;
    }
    public void setResult(String result) {
        this.result = result;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public String getUpdateBy() {
        return updateBy;
    }
    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
    public Integer getIsVisitAgain() {
        return isVisitAgain;
    }
    public void setIsVisitAgain(Integer isVisitAgain) {
        this.isVisitAgain = isVisitAgain;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitPreachformRow.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,192 @@
package com.ruoyi.common.core.service.dto;
import java.util.Date;
/**
 * éšè®¿å­ä»»åŠ¡ã€Œæ‰§è¡Œæ–¹å¼æ˜Žç»†ã€ï¼ˆ{@code service_subtask_preachform})的随访侧行。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * å¯¹åº” {@code ServiceSubtaskPreachform}({@code extends BaseEntity},10 ä¸ªè‡ªæœ‰å­—段 + ç»§æ‰¿å­—段)。
 * æœ¬è¡Œ<b>不继承 BaseEntity</b>(新模块只依赖 ruoyi-common,可以继承,但刻意平铺 â€”— è§ä¸‹ï¼‰ï¼Œ
 * å­—段与 {@code ServiceSubtaskPreachformMapper.xml} çš„
 * {@code selectServiceSubtaskPreachformVo}(L25-42)与 {@code insertServiceSubtaskPreachform}(L81-151)
 * ç”¨åˆ°çš„列<b>逐一对应</b>。
 *
 * <h3>⚠️ å­—段名必须与原实体一致</h3>
 * åŽŸå®žçŽ°ç”¨ Jackson æŠŠ {@code serviceTask.preachformDesc}(JSON ä¸²ï¼‰ç›´æŽ¥ååºåˆ—化成
 * {@code List<ServiceSubtaskPreachform>}。JSON é‡Œçš„键名 = åŽŸå®žä½“çš„å±žæ€§åã€‚
 * æœ¬è¡Œä½œä¸ºæ›¿ä»£ç›®æ ‡ç±»ï¼Œ<b>属性名必须逐字相同</b>,否则 Jackson ä¼šé™é»˜ä¸¢å¼ƒå­—段
 * ï¼ˆä¸æŠ¥é”™ï¼Œåªæ˜¯å€¼ä¸º null â†’ è½åº“少列)。
 *
 * <h3>⚠️ åˆ»æ„ä¸ç»§æ‰¿ ruoyi-common çš„ BaseEntity</h3>
 * ç»§æ‰¿èƒ½å°‘写 6 ä¸ªå­—段,但会带入 {@code searchValue/orgnam e/campusid/pageNum/pageSize/params}
 * ç­‰ä¸Žæœ¬è¡¨æ— å…³çš„属性 â€”— è€Œ Jackson ååºåˆ—化时多出来的属性只在 {@code FAIL_ON_UNKNOWN_PROPERTIES}
 * å¼€å¯æ—¶æ‰æŠ¥é”™ï¼Œé»˜è®¤æ˜¯é™é»˜å¿½ç•¥ã€‚平铺写全,可以让"这个类到底有哪些字段"一眼可数。
 */
public class VisitPreachformRow {
    /** ä¸»é”® */
    private Long id;
    /** å­ä»»åŠ¡ID(原 {@code subid}) */
    private Long subid;
    /** ä»»åŠ¡ID */
    private Long taskid;
    /** æ‰§è¡Œæ–¹å¼ï¼ˆçŸ­ä¿¡/微信/电话…) */
    private String preachform;
    /** æ‰§è¡ŒçŠ¶æ€ï¼ˆåŽŸå®žçŽ°é»˜è®¤ "1",可由调用方覆写) */
    private String sendstate;
    /** æœ¬æ¬¡å‘送时间(原 {@code currentSendTime}) */
    private Date currentSendTime;
    /** æœºæž„ID */
    private String orgid;
    /** åˆ é™¤æ ‡è®° */
    private String delFlag;
    /** æŽ’序 */
    private Long sort;
    /** è¡¥å¿æ—¶é—´ï¼ˆå­—符串,原字段即为 String) */
    private String compensateTime;
    /** å¤‡æ³¨ï¼ˆç»§æ‰¿è‡ª BaseEntity) */
    private String remark;
    /** åˆ›å»ºäººï¼ˆç»§æ‰¿è‡ª BaseEntity) */
    private String createBy;
    /** åˆ›å»ºæ—¶é—´ï¼ˆç»§æ‰¿è‡ª BaseEntity) */
    private Date createTime;
    /** æ›´æ–°äººï¼ˆç»§æ‰¿è‡ª BaseEntity) */
    private String updateBy;
    /** æ›´æ–°æ—¶é—´ï¼ˆç»§æ‰¿è‡ª BaseEntity) */
    private Date updateTime;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Long getSubid() {
        return subid;
    }
    public void setSubid(Long subid) {
        this.subid = subid;
    }
    public Long getTaskid() {
        return taskid;
    }
    public void setTaskid(Long taskid) {
        this.taskid = taskid;
    }
    public String getPreachform() {
        return preachform;
    }
    public void setPreachform(String preachform) {
        this.preachform = preachform;
    }
    public String getSendstate() {
        return sendstate;
    }
    public void setSendstate(String sendstate) {
        this.sendstate = sendstate;
    }
    public Date getCurrentSendTime() {
        return currentSendTime;
    }
    public void setCurrentSendTime(Date currentSendTime) {
        this.currentSendTime = currentSendTime;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getDelFlag() {
        return delFlag;
    }
    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }
    public Long getSort() {
        return sort;
    }
    public void setSort(Long sort) {
        this.sort = sort;
    }
    public String getCompensateTime() {
        return compensateTime;
    }
    public void setCompensateTime(String compensateTime) {
        this.compensateTime = compensateTime;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public String getCreateBy() {
        return createBy;
    }
    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public String getUpdateBy() {
        return updateBy;
    }
    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitScheduleRollResultDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * ä¸€æ¡ã€Œå¾ªçŽ¯ä»»åŠ¡ç»„è®¡åˆ’ã€çš„æ»šåŠ¨ç»“æžœï¼ˆ{@link com.ruoyi.common.core.service.ScheduleRollPort#rollOneSchedule} çš„返回)。
 *
 * <h3>为什么需要它(第 9 æ­¥çš„关键设计说明)</h3>
 * ä»»åŠ¡ç»„è½®æ¬¡æ»šåŠ¨çš„æ ¸å¿ƒ {@code rollOneSchedule} è¦åšä¸€æ¬¡
 * <b>ServiceSubtask çš„全字段深拷贝</b>:
 * <pre>
 * ServiceSubtask copy = JSON.parseObject(JSON.toJSONString(src), ServiceSubtask.class);
 * copy.setId(null); copy.setSenduuid(null); ... // æ¸… 6 ä¸ªå­—段
 * </pre>
 * è€Œ {@code ServiceSubtask} æœ‰ <b>116 ä¸ªå­—段</b>。
 * å¦‚果在两个模块之间传"116 å­—段的 DTO"来做这件事,字段映射本身就是巨大风险面 â€”—
 * è€Œä¸”这个风险<b>换不来任何收益</b>(全字段拷贝的语义由 JSON åºåˆ—化保证,不需要我们逐字段搬运)。
 *
 * <p>所以第 9 æ­¥çš„切分方式是:
 * <ul>
 *   <li><b>下沉到接口</b>({@link com.ruoyi.common.core.service.ScheduleRollPort}):
 *       "查一批待评估的循环计划" + "滚动单个计划"。<b>整个 roll åŠ¨ä½œæ˜¯åŽŸå­çš„</b>,
 *       æ·±æ‹·è´ç•™åœ¨å®žçŽ°ä¾§ï¼ˆsmartor çš„过渡 Bridge),不跨越模块边界;</li>
 *   <li><b>留在本模块</b>({@code ruoyi-inhospvisit}):批次上限、逐条 try/catch、
 *       è®¡æ•°ä¸Žæ—¥å¿— â€”— ä¹Ÿå°±æ˜¯"编排"。</li>
 * </ul>
 *
 * <p>本 DTO åªæ‰¿è½½"用于日志与计数"的信息:原实现在滚动成功后打印
 * <pre>
 * log.info("[任务组-滚动] scheduleId={} æŽ¨è¿›åˆ°ç¬¬ {} è½®, æ–°å»º {} æ¡ subtask", scheduleId, nextLoop, produced);
 * </pre>
 * ä½†é‚£ä¸ªæ–¹æ³•返回的是 {@code boolean},调度侧拿不到 nextLoop / produced。
 * æ¬è¿åŽç”¨æœ¬ DTO æŠŠè¿™ä¸¤ä¸ªå€¼å¸¦å‡ºæ¥ï¼Œè®© inhospvisit èƒ½æ‰“出<b>与原实现完全相同的日志</b>。
 */
public class VisitScheduleRollResultDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** è®¡åˆ’ID */
    private Long scheduleId;
    /** æ»šåŠ¨åŽæŽ¨è¿›åˆ°çš„è½®æ¬¡å·ï¼ˆåŽŸå®žçŽ°é‡Œçš„ {@code nextLoop = currentLoop + 1}) */
    private Long nextLoop;
    /** æœ¬è½®æ–°å»ºçš„ subtask æ¡æ•°ï¼ˆåŽŸå®žçŽ°é‡Œçš„ {@code produced}) */
    private Integer produced;
    /** æ˜¯å¦çœŸçš„æŽ¨è¿›äº†ï¼ˆåŽŸæ–¹æ³•è¿”å›žçš„ boolean) */
    private boolean rolled;
    /** æœªæŽ¨è¿›çš„原因(仅用于日志排查;推进成功时为 null) */
    private String skipReason;
    public static VisitScheduleRollResultDto skipped(Long scheduleId, String reason) {
        VisitScheduleRollResultDto d = new VisitScheduleRollResultDto();
        d.setScheduleId(scheduleId);
        d.setRolled(false);
        d.setSkipReason(reason);
        return d;
    }
    public static VisitScheduleRollResultDto rolled(Long scheduleId, long nextLoop, int produced) {
        VisitScheduleRollResultDto d = new VisitScheduleRollResultDto();
        d.setScheduleId(scheduleId);
        d.setNextLoop(nextLoop);
        d.setProduced(produced);
        d.setRolled(true);
        return d;
    }
    public Long getScheduleId() {
        return scheduleId;
    }
    public void setScheduleId(Long scheduleId) {
        this.scheduleId = scheduleId;
    }
    public Long getNextLoop() {
        return nextLoop;
    }
    public void setNextLoop(Long nextLoop) {
        this.nextLoop = nextLoop;
    }
    public Integer getProduced() {
        return produced;
    }
    public void setProduced(Integer produced) {
        this.produced = produced;
    }
    public boolean isRolled() {
        return rolled;
    }
    public void setRolled(boolean rolled) {
        this.rolled = rolled;
    }
    public String getSkipReason() {
        return skipReason;
    }
    public void setSkipReason(String skipReason) {
        this.skipReason = skipReason;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitSubtaskDraft.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,808 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
import java.util.Date;
/**
 * ä¸€æ¡å¾…落库随访子任务的草稿({@link VisitTaskCreateApi#createSubtask} çš„入参)。
 * <p>
 * å­—段是<b>逐个对照迁移前 {@code PatMedInhospServiceImpl#boxedServiceSubtask}(L1108-1222)
 * ä¸Ž {@code #boxedServiceSubtaskForOp}(L1225-1300)的 setter è°ƒç”¨</b>倒推出来的。
 * è¯¥æ–¹æ³•是出入院随访唯一可能改变线上行为的地方,所以这里刻意<b>不做字段精简</b>:
 * å°‘一个字段 â†’ å¯¹åº” setter é™é»˜å¤±æ•ˆ â†’ çº¿ä¸Šå­ä»»åŠ¡å†…å®¹å˜åŒ–ï¼Œä¸”ä¸æŠ¥é”™ã€‚
 *
 * <h3>迁移时的对应关系(逐条核对用)</h3>
 * <pre>
 * åŽŸ patMedInhosp1/patArchive/serviceTask getter  â†’  æœ¬ DTO å­—段
 * </pre>
 *
 * @see com.ruoyi.common.core.service.VisitTaskCreateApi#createSubtask(VisitSubtaskDraft)
 */
public class VisitSubtaskDraft implements Serializable {
    private static final long serialVersionUID = 1L;
    // ==================== ä»»åŠ¡æ¥æºï¼ˆåŽŸ serviceTask) ====================
    /**
     * å­ä»»åŠ¡ID。
     * <p>
     * åªæœ‰"先 insert、再补字段回写"那条链路(原 L776-793)才需要;
     * æ–°å¢žæ—¶ç•™ç©ºã€‚{@code insertServiceSubtask} ç”¨çš„æ˜¯ {@code useGeneratedKeys},
     * è½åº“后这个字段会被回填到实体上,调用方据此再 update。
     */
    private Long id;
    /** ä»»åŠ¡ID(原 taskid) */
    private Long taskid;
    /** æœºæž„ID(原 serviceTask.orgid) */
    private String orgid;
    /** é™¢åŒºID(原 serviceTask.campusid,后被 patMedInhosp.campusid è¦†ç›–) */
    private String campusid;
    // ==================== æ‚£è€…(原 patArchive) ====================
    /** æ‚£è€…档案ID(原 patArchive.id) */
    private Long patid;
    /** æŽ¥æ”¶äººå§“名(原 patArchive.name) */
    private String sendname;
    /** èº«ä»½è¯å·ï¼ˆåŽŸ patArchive.idcardno) */
    private String sfzh;
    /** æ‰‹æœºå·ï¼ˆåŽŸ patArchive.telcode,空则取 relativetelcode) */
    private String phone;
    /** æ€§åˆ«ï¼ˆåŽŸ patArchive.sex) */
    private Long sex;
    /** å¹´é¾„(原 patArchive.age) */
    private Long age;
    // ==================== å‡ºå…¥é™¢å…³è”(原 patMedInhosp) ====================
    /** å‡ºå…¥é™¢è®°å½•ID(原 inhospid) */
    private Long inhospid;
    /** ä½é™¢æµæ°´å· */
    private String serialnum;
    // ==================== åŒ»æŠ¤ï¼ˆåŽŸ patMedInhosp) ====================
    /** è´£ä»»æŠ¤å£«ID */
    private String nurseId;
    /** è´£ä»»æŠ¤å£«å§“名 */
    private String nurseName;
    /** åŒ»ç”Ÿç¼–码 */
    private String drcode;
    /** åŒ»ç”Ÿå§“名 */
    private String drname;
    /** ç®¡ç†åŒ»ç”Ÿå§“名 */
    private String managementDoctor;
    /** ç®¡ç†åŒ»ç”Ÿç¼–码 */
    private String managementDoctorCode;
    // ==================== ç§‘室/病区(原 patMedInhosp,type 4/5 ä¼šæ”¹å†™ï¼‰ ====================
    /** ç§‘室编码(type 1/2/3 å– leave ç»´åº¦ï¼›type 4/5 å–入院维度) */
    private String deptcode;
    /** ç§‘室名称 */
    private String deptname;
    /** ç¦»é™¢ç—…区编码(type 4/5 å–入院病区) */
    private String leavehospitaldistrictcode;
    /** ç¦»é™¢ç—…区名称 */
    private String leavehospitaldistrictname;
    // ==================== è¯Šæ–­ï¼ˆåŽŸ patMedInhosp) ====================
    /** ç¦»é™¢ ICD10 ç¼–码 */
    private String leaveicd10code;
    /** ç¦»é™¢è¯Šæ–­åç§° */
    private String leavediagname;
    /** è¯Šæ–­åç§° */
    private String diagname;
    // ==================== æ¨¡æ¿ï¼ˆåŽŸ serviceTask) ====================
    /** é—®å·æ¨¡æ¿ID */
    private Long templateid;
    /** é—®å·æ¨¡æ¿åç§° */
    private String templatename;
    /** è¯­éŸ³æ¨¡æ¿ID */
    private Long libtemplateid;
    // ==================== æ—¶é—´ ====================
    /** å…¥é™¢æ—¶é—´ï¼ˆåŽŸ patMedInhosp.starttime) */
    private Date starttime;
    /** å‡ºé™¢æ—¶é—´ï¼ˆåŽŸ patMedInhosp.endtime) */
    private Date endtime;
    /** é•¿æœŸä»»åŠ¡å‘é€æ—¶é—´ï¼ˆåŽŸ longSendTime) */
    private Date longSendTime;
    /** éšè®¿æ—¶é—´ï¼ˆåŽŸ visitTime) */
    private Date visitTime;
    // ==================== çŠ¶æ€/类型 ====================
    /** å‘送状态(原 sendstate) */
    private Long sendstate;
    /**
     * æäº¤æ ‡è®°ï¼ˆåŽŸ {@code submit},实体里是 {@code Long})。
     * <p>
     * âš ï¸ è¿ç§»å‰ {@code writeInSubTask} é‡Œå¯¹å®ƒçš„写法是 {@code serviceSubtask.setSubmit(1)} â€”—
     * ä¸€ä¸ª int å­—面量被自动装箱/加宽成 {@code Long},历史上编译能过。
     * æ¬è¿æ—¶è‹¥æŠŠå®ƒå†™æˆ {@code Integer} å°±ä¼šé™é»˜ä¸¢å­—段,故此处保持 {@code Long}。
     */
    private Long submit;
    /** æœåŠ¡ç±»åž‹ï¼ˆåŽŸ serviceType) */
    private String serviceType;
    /** ç–¾ç—…类型(原 diagType) */
    private String diagType;
    /** ä½é™¢ç±»åž‹ï¼ˆåŽŸ hospType,"2" æ™®é€š / "6" å…¥é™¢ï¼‰ */
    private String hospType;
    /** æ‰§è¡Œæ–¹å¼ï¼ˆåŽŸ preachform) */
    private String preachform;
    // ==================== éšè®¿æ ‡è®°ï¼ˆåŽŸ writeInSubTask åœ¨å…¥åº“前设置的几个) ====================
    /** å°±è¯Šæ¬¡æ•°ï¼ˆåŽŸ visitCount) */
    private Integer visitCount;
    /** æ˜¯å¦å†æ¬¡éšè®¿ï¼ˆåŽŸ isVisitAgain) */
    private Integer isVisitAgain;
    /** å°±è¯Šç§‘室编码(原 visitDeptCode,由 type åˆ†æ´¾å†³å®šï¼‰ */
    private String visitDeptCode;
    /** å°±è¯Šç§‘室名称(原 visitDeptName) */
    private String visitDeptName;
    /** ä»»åŠ¡çŠ¶æ€ï¼ˆåŽŸ taskSituation;宣教=7,被过滤患者=6) */
    private Integer taskSituation;
    /** ç»“果(原 result,"error" è¡¨ç¤ºä¸æ‰§è¡Œï¼‰ */
    private String result;
    /** å¤‡æ³¨ï¼ˆåŽŸ remark) */
    private String remark;
    // ==================== æ‰‹æœ¯ï¼ˆåŽŸ patMedOperationItem,仅手术随访用) ====================
    /** æ‰‹æœ¯æ˜Žç»†ID(原 operationItemId) */
    private Long operationItemId;
    // ==================== å®¡è®¡ ====================
    /** åˆ›å»ºäººï¼ˆåŽŸ createBy) */
    private String createBy;
    /** æ›´æ–°äººï¼ˆåŽŸ updateBy) */
    private String updateBy;
    // ==================== å…œåº• ====================
    /**
     * é•¿æœŸä»»åŠ¡å¤±è´¥åŽŸå› ï¼ˆåŽŸ {@code patMedInhosp.longTaskReason})。
     * <p>
     * ã€æ³¨æ„ã€‘这不是子任务的字段。它在迁移前是 {@code writeInSubTask} ä»Žå…¥å‚实体上读出来、
     * æ‹¼ä¸Šé”™è¯¯æ–‡æ¡ˆåŽå†å†™å›ž {@code pat_med_inhosp} çš„。放在这里是为了让
     * {@code VisitTaskCreateApi} çš„实现能拿到它去写 B7 æ ‡è®°ï¼›
     * <b>过渡实现不会把它写入 service_subtask</b>。
     */
    private String longTaskReason;
    // ==========================================================================
    // DtoConversionUtils æ‹·å…¥ã€ä½†æœªè¢«æ˜¾å¼è¦†ç›–çš„ 18 ä¸ªå­—段
    // ==========================================================================
    /**
     * âš ï¸ <b>这一批字段的来源必须说清楚(踩过的坑)</b>
     * <p>
     * è¿ç§»å‰ {@code boxedServiceSubtask} / {@code boxedServiceSubtaskForOp} çš„第一行是:
     * <pre>
     * ServiceSubtask serviceSubtask = DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class);
     * </pre>
     * å®ƒæŠŠ {@code ServiceTask} ä¸Ž {@code ServiceSubtask} <b>同名</b>的 33 ä¸ªå­—段整批拷了过来,
     * å…¶ä¸­ <b>18 ä¸ªä¹‹åŽå†ä¹Ÿæ²¡è¢«æ˜¾å¼ set è¦†ç›–</b> â€”— ä¹Ÿå°±æ˜¯è¯´ï¼Œè¿™ 18 ä¸ªå€¼æ˜¯
     * <b>靠"拷贝"带进子任务并落库的</b>。
     * <p>
     * é€å­—段核对过,这 18 ä¸ªé‡Œè¿˜æœ‰<b>类型不一致</b>的:
     * {@code pid}({@code Integer â†’ Long})、{@code sendType}({@code Long â†’ String}),
     * è¯´æ˜Ž {@code sourceToTarget} å†…部做了类型转换,不是简单的同名赋值。
     * <p>
     * <b>如果不把它们显式带过去,这些值就会丢 â†’ è½åº“的子任务内容与迁移前不一致,而且不会报错。</b>
     * æ‰€ä»¥è¿™é‡Œå…¨éƒ¨æ˜¾å¼åˆ—出,让"每个字段是怎么进子任务的"完全可见,
     * å¹¶ç”± {@code sql/_probe/verify_inhospvisit_semantics.py} é€æ¡æ–­è¨€ã€‚
     * <p>
     * å¯¹åº”的原实现位置:{@code PatMedInhospServiceImpl} L1113(boxed)与 L1226(boxedForOp)。
     */
    /** åˆ é™¤æ ‡è®°ï¼ˆåŽŸ serviceTask.delFlag) */
    private String delFlag;
    /*
     * æ³¨æ„ï¼šdiagType å·²ç»åœ¨ä¸Šé¢"状态/类型"那一组里声明过了(同名字段不能重复声明),
     * è¿™é‡Œåˆ»æ„ä¸å†é‡å¤ã€‚它同样属于"DtoConversionUtils æ‹·å…¥ä½†æœªè¢«æ˜¾å¼è¦†ç›–"的那一批,
     * ç”Ÿæˆè„šæœ¬å½“æ—¶æ²¡æŸ¥é‡ï¼Œå¯¼è‡´è¿‡ä¸€æ¬¡ç¼–è¯‘å¤±è´¥ â€”— å·²ç”± sql/_probe/_check_draft_duplicates.py å…œä½ã€‚
     */
    /** å‡ºé™¢æ—¶é—´åŒºé—´ç»ˆç‚¹ï¼ˆåŽŸ serviceTask.endOutHospTime) */
    private java.util.Date endOutHospTime;
    /** GUID(原 serviceTask.guid) */
    private String guid;
    /** æ˜¯å¦å·²ä¸Šä¼ ï¼ˆåŽŸ serviceTask.isupload) */
    private Long isupload;
    /** ç¦»é™¢ç—…区编码列表(原 serviceTask.leavehospitaldistrictcodes) */
    private java.util.List<String> leavehospitaldistrictcodes;
    /** ç¦»é™¢ç§‘室编码列表(原 serviceTask.leaveldeptcodes) */
    private java.util.List<String> leaveldeptcodes;
    /** è¯­éŸ³æ¨¡æ¿åç§°ï¼ˆåŽŸ serviceTask.libtemplatename) */
    private String libtemplatename;
    /** åˆ†é¡µé¡µç ï¼ˆåŽŸ serviceTask.pageNum) */
    private Integer pageNum;
    /** åˆ†é¡µé¡µé•¿ï¼ˆåŽŸ serviceTask.pageSize) */
    private Integer pageSize;
    /** pid(原 serviceTask.pid æ˜¯ Integer,目标是 Long â€”— sourceToTarget åšäº†è½¬æ¢ï¼‰ */
    private Long pid;
    /** å‘送类型(原 serviceTask.sendType æ˜¯ Long,目标是 String â€”— åŒæ ·åšäº†è½¬æ¢ï¼‰ */
    private String sendType;
    /** å‡ºé™¢æ—¶é—´åŒºé—´èµ·ç‚¹ï¼ˆåŽŸ serviceTask.startOutHospTime) */
    private java.util.Date startOutHospTime;
    /** ä»»åŠ¡æè¿°ï¼ˆåŽŸ serviceTask.taskDesc) */
    private String taskDesc;
    /** ä»»åŠ¡åç§°ï¼ˆåŽŸ serviceTask.taskName) */
    private String taskName;
    /** æ–‡æœ¬å‚数(原 serviceTask.textParam) */
    private String textParam;
    /** ä»»åŠ¡ç±»åž‹ï¼ˆåŽŸ serviceTask.type) */
    private String type;
    /** ä¸Šä¼ æ—¶é—´ï¼ˆåŽŸ serviceTask.uploadTime) */
    private java.util.Date uploadTime;
    // ---------- è¡¥é½çš„字段 ----------
    /** åˆ›å»ºæ—¶é—´ï¼ˆåŽŸ L1157 setCreateTime(new Date())) */
    private java.util.Date createTime;
    /** æ›´æ–°æ—¶é—´ï¼ˆåŽŸ L1158/L1163 setUpdateTime(new Date())) */
    private java.util.Date updateTime;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Long getTaskid() {
        return taskid;
    }
    public void setTaskid(Long taskid) {
        this.taskid = taskid;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getCampusid() {
        return campusid;
    }
    public void setCampusid(String campusid) {
        this.campusid = campusid;
    }
    public Long getPatid() {
        return patid;
    }
    public void setPatid(Long patid) {
        this.patid = patid;
    }
    public String getSendname() {
        return sendname;
    }
    public void setSendname(String sendname) {
        this.sendname = sendname;
    }
    public String getSfzh() {
        return sfzh;
    }
    public void setSfzh(String sfzh) {
        this.sfzh = sfzh;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public Long getSex() {
        return sex;
    }
    public void setSex(Long sex) {
        this.sex = sex;
    }
    public Long getAge() {
        return age;
    }
    public void setAge(Long age) {
        this.age = age;
    }
    public Long getInhospid() {
        return inhospid;
    }
    public void setInhospid(Long inhospid) {
        this.inhospid = inhospid;
    }
    public String getSerialnum() {
        return serialnum;
    }
    public void setSerialnum(String serialnum) {
        this.serialnum = serialnum;
    }
    public String getNurseId() {
        return nurseId;
    }
    public void setNurseId(String nurseId) {
        this.nurseId = nurseId;
    }
    public String getNurseName() {
        return nurseName;
    }
    public void setNurseName(String nurseName) {
        this.nurseName = nurseName;
    }
    public String getDrcode() {
        return drcode;
    }
    public void setDrcode(String drcode) {
        this.drcode = drcode;
    }
    public String getDrname() {
        return drname;
    }
    public void setDrname(String drname) {
        this.drname = drname;
    }
    public String getManagementDoctor() {
        return managementDoctor;
    }
    public void setManagementDoctor(String managementDoctor) {
        this.managementDoctor = managementDoctor;
    }
    public String getManagementDoctorCode() {
        return managementDoctorCode;
    }
    public void setManagementDoctorCode(String managementDoctorCode) {
        this.managementDoctorCode = managementDoctorCode;
    }
    public String getDeptcode() {
        return deptcode;
    }
    public void setDeptcode(String deptcode) {
        this.deptcode = deptcode;
    }
    public String getDeptname() {
        return deptname;
    }
    public void setDeptname(String deptname) {
        this.deptname = deptname;
    }
    public String getLeavehospitaldistrictcode() {
        return leavehospitaldistrictcode;
    }
    public void setLeavehospitaldistrictcode(String leavehospitaldistrictcode) {
        this.leavehospitaldistrictcode = leavehospitaldistrictcode;
    }
    public String getLeavehospitaldistrictname() {
        return leavehospitaldistrictname;
    }
    public void setLeavehospitaldistrictname(String leavehospitaldistrictname) {
        this.leavehospitaldistrictname = leavehospitaldistrictname;
    }
    public String getLeaveicd10code() {
        return leaveicd10code;
    }
    public void setLeaveicd10code(String leaveicd10code) {
        this.leaveicd10code = leaveicd10code;
    }
    public String getLeavediagname() {
        return leavediagname;
    }
    public void setLeavediagname(String leavediagname) {
        this.leavediagname = leavediagname;
    }
    public String getDiagname() {
        return diagname;
    }
    public void setDiagname(String diagname) {
        this.diagname = diagname;
    }
    public Long getTemplateid() {
        return templateid;
    }
    public void setTemplateid(Long templateid) {
        this.templateid = templateid;
    }
    public String getTemplatename() {
        return templatename;
    }
    public void setTemplatename(String templatename) {
        this.templatename = templatename;
    }
    public Long getLibtemplateid() {
        return libtemplateid;
    }
    public void setLibtemplateid(Long libtemplateid) {
        this.libtemplateid = libtemplateid;
    }
    public Date getStarttime() {
        return starttime;
    }
    public void setStarttime(Date starttime) {
        this.starttime = starttime;
    }
    public Date getEndtime() {
        return endtime;
    }
    public void setEndtime(Date endtime) {
        this.endtime = endtime;
    }
    public Date getLongSendTime() {
        return longSendTime;
    }
    public void setLongSendTime(Date longSendTime) {
        this.longSendTime = longSendTime;
    }
    public Date getVisitTime() {
        return visitTime;
    }
    public void setVisitTime(Date visitTime) {
        this.visitTime = visitTime;
    }
    public Long getSendstate() {
        return sendstate;
    }
    public void setSendstate(Long sendstate) {
        this.sendstate = sendstate;
    }
    public Long getSubmit() {
        return submit;
    }
    public void setSubmit(Long submit) {
        this.submit = submit;
    }
    public String getDiagType() {
        return diagType;
    }
    public void setDiagType(String diagType) {
        this.diagType = diagType;
    }
    public Integer getVisitCount() {
        return visitCount;
    }
    public void setVisitCount(Integer visitCount) {
        this.visitCount = visitCount;
    }
    public Integer getIsVisitAgain() {
        return isVisitAgain;
    }
    public void setIsVisitAgain(Integer isVisitAgain) {
        this.isVisitAgain = isVisitAgain;
    }
    public String getVisitDeptCode() {
        return visitDeptCode;
    }
    public void setVisitDeptCode(String visitDeptCode) {
        this.visitDeptCode = visitDeptCode;
    }
    public String getVisitDeptName() {
        return visitDeptName;
    }
    public void setVisitDeptName(String visitDeptName) {
        this.visitDeptName = visitDeptName;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public String getHospType() {
        return hospType;
    }
    public void setHospType(String hospType) {
        this.hospType = hospType;
    }
    public String getPreachform() {
        return preachform;
    }
    public void setPreachform(String preachform) {
        this.preachform = preachform;
    }
    public Integer getTaskSituation() {
        return taskSituation;
    }
    public void setTaskSituation(Integer taskSituation) {
        this.taskSituation = taskSituation;
    }
    public String getResult() {
        return result;
    }
    public void setResult(String result) {
        this.result = result;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public Long getOperationItemId() {
        return operationItemId;
    }
    public void setOperationItemId(Long operationItemId) {
        this.operationItemId = operationItemId;
    }
    public String getCreateBy() {
        return createBy;
    }
    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }
    public String getUpdateBy() {
        return updateBy;
    }
    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }
    public String getLongTaskReason() {
        return longTaskReason;
    }
    public void setLongTaskReason(String longTaskReason) {
        this.longTaskReason = longTaskReason;
    }
    // ---------- ä¸Šè¿° 18 ä¸ªæ‰¿è½½å­—段的访问器 ----------
    public String getDelFlag() {
        return delFlag;
    }
    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }
    public java.util.Date getEndOutHospTime() {
        return endOutHospTime;
    }
    public void setEndOutHospTime(java.util.Date endOutHospTime) {
        this.endOutHospTime = endOutHospTime;
    }
    public String getGuid() {
        return guid;
    }
    public void setGuid(String guid) {
        this.guid = guid;
    }
    public Long getIsupload() {
        return isupload;
    }
    public void setIsupload(Long isupload) {
        this.isupload = isupload;
    }
    public java.util.List<String> getLeavehospitaldistrictcodes() {
        return leavehospitaldistrictcodes;
    }
    public void setLeavehospitaldistrictcodes(java.util.List<String> leavehospitaldistrictcodes) {
        this.leavehospitaldistrictcodes = leavehospitaldistrictcodes;
    }
    public java.util.List<String> getLeaveldeptcodes() {
        return leaveldeptcodes;
    }
    public void setLeaveldeptcodes(java.util.List<String> leaveldeptcodes) {
        this.leaveldeptcodes = leaveldeptcodes;
    }
    public String getLibtemplatename() {
        return libtemplatename;
    }
    public void setLibtemplatename(String libtemplatename) {
        this.libtemplatename = libtemplatename;
    }
    public Integer getPageNum() {
        return pageNum;
    }
    public void setPageNum(Integer pageNum) {
        this.pageNum = pageNum;
    }
    public Integer getPageSize() {
        return pageSize;
    }
    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
    public Long getPid() {
        return pid;
    }
    public void setPid(Long pid) {
        this.pid = pid;
    }
    public String getSendType() {
        return sendType;
    }
    public void setSendType(String sendType) {
        this.sendType = sendType;
    }
    public java.util.Date getStartOutHospTime() {
        return startOutHospTime;
    }
    public void setStartOutHospTime(java.util.Date startOutHospTime) {
        this.startOutHospTime = startOutHospTime;
    }
    public String getTaskDesc() {
        return taskDesc;
    }
    public void setTaskDesc(String taskDesc) {
        this.taskDesc = taskDesc;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public String getTextParam() {
        return textParam;
    }
    public void setTextParam(String textParam) {
        this.textParam = textParam;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public java.util.Date getUploadTime() {
        return uploadTime;
    }
    public void setUploadTime(java.util.Date uploadTime) {
        this.uploadTime = uploadTime;
    }
    // ---------- è¡¥é½å­—段的访问器 ----------
    public java.util.Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(java.util.Date createTime) {
        this.createTime = createTime;
    }
    public java.util.Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(java.util.Date updateTime) {
        this.updateTime = updateTime;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/core/service/dto/VisitTaskRuleDto.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
package com.ruoyi.common.core.service.dto;
import java.io.Serializable;
/**
 * éšè®¿ä»»åŠ¡æ‰§è¡Œè§„åˆ™ï¼ˆ{@code service_task_rule})的随访侧视图。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * å¯¹åº” {@code ServiceTaskRule},由 {@code PatMedInhospServiceImpl#addSubTaskByRule}
 * é€šè¿‡ {@code selectServiceTaskRuleByDeptOrWard} æŸ¥è¯¢ã€‚该查询<b>最多返回一条</b>。
 *
 * <h3>⚠️ å­—段名 {@code wradRule} æ˜¯åŽŸå®žä½“çš„æ‹¼å†™é”™è¯¯ï¼Œåˆ»æ„ä¿æŒåŒå</h3>
 * åŽŸ {@code ServiceTaskRule} é‡Œç—…区规则字段就叫 {@code wradRule}(缺了 d),
 * ä¸” {@code addSubTaskByRule} é‡Œè¯»çš„æ˜¯ {@code rule.getWradRule()}。
 * è¿™é‡Œä¿æŒåŒåï¼Œé¿å…"看起来像新字段"而读错。
 *
 * <h3>三个规则值的语义(原实现 L1721-1726)</h3>
 * <pre>
 * "1" = é¦–要规则(先执行,成功则次要规则跳过)
 * "2" = æ¬¡è¦è§„则(首要全失败时才执行)
 * å…¶å®ƒ/null = ä¸å‚与
 * </pre>
 */
public class VisitTaskRuleDto implements Serializable {
    private static final long serialVersionUID = 1L;
    /** è§„则ID */
    private Long id;
    /** ç§‘室规则:"1"=首要 "2"=次要 */
    private String deptRule;
    /** ç—…区规则(⚠️ åŽŸå®žä½“æ‹¼å†™å³ä¸º {@code wradRule}) */
    private String wradRule;
    /** ç–¾ç—…规则 */
    private String diagRule;
    /** ç¦»é™¢ç§‘室编码 */
    private String deptcode;
    /** ç¦»é™¢ç—…区编码 */
    private String wardcode;
    /** æœºæž„ID */
    private String orgid;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getDeptRule() {
        return deptRule;
    }
    public void setDeptRule(String deptRule) {
        this.deptRule = deptRule;
    }
    public String getWradRule() {
        return wradRule;
    }
    public void setWradRule(String wradRule) {
        this.wradRule = wradRule;
    }
    public String getDiagRule() {
        return diagRule;
    }
    public void setDiagRule(String diagRule) {
        this.diagRule = diagRule;
    }
    public String getDeptcode() {
        return deptcode;
    }
    public void setDeptcode(String deptcode) {
        this.deptcode = deptcode;
    }
    public String getWardcode() {
        return wardcode;
    }
    public void setWardcode(String wardcode) {
        this.wardcode = wardcode;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
}
ruoyi-common/src/main/java/com/ruoyi/common/utils/PhoneUtils.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,311 @@
package com.ruoyi.common.utils;
import com.ruoyi.common.utils.HttpUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.HttpEntity;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.mail.Multipart;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Component
public class PhoneUtils {
    @Value("${phoneIP}")
    private String phoneIP;
    @Value("${phonePort}")
    private String phonePort;
    /**
     * æ·»åŠ åˆ†æœºadd_user
     */
    public String addUser(String user, String pwd) {
        Map<String, Object> map = new HashMap<>();
        map.put("user", user);
        map.put("password", pwd);
        return sendReq(map, "/tel/ai_api/add_user");
    }
    /**
     * åˆ é™¤åˆ†æœºdelete_user
     *
     * @return
     */
    public String delUser(String user) {
        Map<String, Object> map = new HashMap<>();
        map.put("user", user);
        return sendReq(map, "/tel/ai_api/delete_user");
    }
    /**
     * 3、检查分机是否存在check_user
     *
     * @return
     */
    public String checkUser(String user) {
        Map<String, Object> map = new HashMap<>();
        map.put("user", user);
        return sendReq(map, "/tel/ai_api/check_user");
    }
    /**
     * åˆ—出全部坐席或者指定坐席 list_agent
     *
     * @param name
     * @return
     */
    public String listAgent(String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        return sendReq(map, "/tel/ai_api/list_agent");
    }
    /**
     * æ·»åŠ åå¸­add_agent
     * å‘系统中添加一个坐席,后续需要set_agent_state,set_agent_status,set_agent_contact, tier_add è®¾ç½®åå¸­çж体,sip_uri和把坐席加入/绑定一个呼叫队列
     *
     * @param name
     * @return
     */
    public String addAgent(String name, String type) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        if (StringUtils.isEmpty(type)) map.put("type", "callback");
        return sendReq(map, "/tel/ai_api/add_agent");
    }
    /**
     * åˆ é™¤åå¸­del_agent
     * ä»Žå‘¼å«é˜Ÿåˆ—删除坐席
     *
     * @param name
     * @return
     */
    public String delAgent(String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        return sendReq(map, "/tel/ai_api/del_agent");
    }
    /**
     * èŽ·å–åå¸­å½“å‰æ‰€åœ¨é€šè¯çš„uuid
     *
     * @param name
     * @return
     */
    public String getAgentUUID(String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        return sendReq(map, "/tel/ai_api/get_agent_uuid");
    }
    /**
     * æ·»åŠ åå¸­åŽ(add_agent),需要设置坐席地址(sip uri),坐席才可以被访问到
     *
     * @param name
     * @param contact
     * @return
     */
    public String setAgentUUID(String name, String contact) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("contact", contact);
        return sendReq(map, "/tel/ai_api/set_agent_contact");
    }
    /**
     * è®¾ç½®åå¸­çš„status,有固定几个状态
     *
     * @param name
     * @param status
     * @return
     */
    public String setAgentStatus(String name, String status) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("status", status);
        return sendReq(map, "/tel/ai_api/set_agent_status");
    }
    /**
     * èŽ·å–åå¸­çŠ¶æ€
     *
     * @param name
     * @return
     */
    public String getAgentStatus(String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        return sendReq(map, "/tel/ai_api/get_agent_status");
    }
    /**
     * è®¾è®¾ç½®åå¸­
     *
     * @param name
     * @param state
     * @return
     */
    public String setAgentState(String name, String state) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        map.put("state", state);
        return sendReq(map, "/tel/ai_api/set_agent_state");
    }
    /**
     * èŽ·å–åå¸­çŠ¶æ€
     *
     * @param name
     * @return
     */
    public String getAgentState(String name) {
        Map<String, Object> map = new HashMap<>();
        map.put("name", name);
        return sendReq(map, "/tel/ai_api/get_agent_state");
    }
    /**
     * æ‰‹åЍ外呼
     *
     * @return
     */
    public String manualOutbound(String kg_uuid, String kg_file, String data, String app_id, String ani, String special_ch, String sign, String extension, String dnis, Boolean force_call) {
        Map<String, Object> map = new HashMap<>();
        map.put("kg_uuid", kg_uuid);
        map.put("kg_file", kg_file);
        map.put("data", data);
        map.put("app_id", app_id);
        map.put("ani", ani);
        map.put("special_ch", special_ch);
        map.put("sign", sign);
        map.put("extension", extension);
        map.put("dnis", dnis);
        map.put("force_call", force_call);
        if (force_call == null) map.put("force_call", true);
        return sendReq(map, "/tel/ai_api/manual_outbound");
    }
    /**
     * tts合成和播放
     *
     * @param fileText
     * @param uuid
     * @return
     */
    public String ttsPlayback(String fileText, String uuid) {
        System.out.println("=====================问题内容:" + fileText + "UUID ; " + uuid);
        Map<String, Object> map = new HashMap<>();
        map.put("text", fileText);
        map.put("uuid", uuid);
        return sendReq(map, "/tel/ai_api/tts_playback");
    }
    /**
     * wav文件播放接口
     *
     * @param wav_file
     * @param uuid
     * @return
     */
    public String manualOutbound(Multipart wav_file, String uuid) {
        Map<String, Object> map = new HashMap<>();
        map.put("wav_file", wav_file);
        map.put("uuid", uuid);
        return sendReq(map, "/tel/ai_api/playback");
    }
    /**
     * æœºå™¨äººå¤–呼 ob
     *
     * @return
     */
    public String ob(String kg_uuid, String kg_file, String data, String app_id, String ani, String special_ch, String sign, String dnis, String call_uuid, Boolean force_call) {
        Map<String, Object> map = new HashMap<>();
        map.put("kg_uuid", kg_uuid);
        map.put("kg_file", kg_file);
        map.put("data", data);
        map.put("app_id", app_id);
        map.put("ani", ani);
        map.put("special_ch", special_ch);
        map.put("sign", sign);
        map.put("dnis", dnis);
        map.put("call_uuid", call_uuid);
        map.put("force_call", force_call);
        map.put("fs_node_id", 8021);
        if (force_call == null) map.put("force_call", true);
        return sendReq(map, "/tel/ai_api/outbound");
    }
    /**
     * æŒ‚断通话
     *
     * @return
     */
    public String hangup(String kg_uuid, String dnis, String data, String app_id, String ani, String special_ch, String sign, String call_uuid) {
        Map<String, Object> map = new HashMap<>();
        map.put("kg_uuid", kg_uuid);
        map.put("kg_file", kg_uuid);
        map.put("dnis", dnis);
        map.put("data", data);
        map.put("app_id", app_id);
        map.put("ani", ani);
        map.put("special_ch", special_ch);
        map.put("sign", sign);
        map.put("call_uuid", call_uuid);
        map.put("fs_node_id", "");
//        map.put("app_id", app_id);
//        map.put("kg_uuid", kg_uuid);
//        map.put("ani", ani);
//        map.put("dnis", dnis);
//        map.put("call_direction", call_direction_str);
//        map.put("hangup_cause", hangup_cause);
//        map.put("endpoint_disposition", endpoint_disposition);
//        map.put("wait_msec", wait_msec);
//        map.put("duration_msec", duration_msec);
//        map.put("answer_msec", answer_msec);
//        map.put("bill_msec", bill_msec);
//        map.put("start_stamp", start_stamp);
//        map.put("answered_stamp", answered_stamp);
//        map.put("end_stamp", end_stamp);
//        map.put("vad_total_talk_time_msec", vad_total_talk_time_msec);
//        map.put("time_stamp", time_stamp);
        return sendReq(map, "/tel/ai_api/hangup");
    }
    private String sendReq(Map<String, Object> map, String path) {
        HttpEntity<Map<String, Object>> req = new HttpEntity<>(getHead(), map);
        if (StringUtils.isEmpty(phoneIP)) {
            phoneIP = "http://124.220.50.51";
            phonePort = "8001";
        }
        HttpEntity<String> stringHttpEntity = HttpUtil.postJsonRequestV2(phoneIP + ":" + phonePort + path, req, String.class);
        String responseBody = null;
        try {
            responseBody = new String(stringHttpEntity.getBody().getBytes("ISO-8859-1"), "UTF-8");
            System.out.println("电话信息返回 : " + responseBody);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return new String(stringHttpEntity.getBody().getBytes(StandardCharsets.UTF_8));
    }
    private Map<String, String> getHead() {
        HashMap<String, String> header = new HashMap<>();
        header.put("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko");
        header.put("Content-Type", "application/json");
        return header;
    }
}
ruoyi-inhospvisit/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,71 @@
<?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-inhospvisit</artifactId>
    <description>
        å‡ºå…¥é™¢éšè®¿æ¨¡å—:出院随访(科室/病区/疾病)+ å…¥é™¢éšè®¿ + å†å…¥é™¢åœæœº + ä»»åŠ¡ç»„é¦–è½®çº³å…¥/轮次滚动的唯一实现处。
        æœ¬æ¨¡å—自带 Mapper ä¸Ž SQL(只读 pat_med_inhosp çš„随访字段 + å†™éšè®¿çŠ¶æ€æ ‡å¿—ä½ï¼‰ï¼Œ
        å…¶å®ƒæ¨¡å—需要出入院随访能力只能通过 com.ruoyi.inhospvisit.api è°ƒç”¨ã€‚
        æ”¯æŒé€šè¿‡ sys_config å‚数表里的 inhospvisit.enabled æ•´ä½“关闭(运行时读取,改完最多 10 ç§’生效)。
        ä¾èµ–方向(务必保持):
          æœ¬æ¨¡å—【只依赖 ruoyi-common】,绝不依赖 smartor â€”— å¦åˆ™ä¼šå½¢æˆ
          smartor -> ruoyi-inhospvisit -> smartor çš„循环依赖,Maven ç›´æŽ¥æž„建失败。
          éœ€è¦"建随访任务 / å®£æ•™ / æ‰‹æœ¯éšè®¿"时,一律走 ruoyi-common é‡Œä¸‹æ²‰çš„æŽ¥å£
          ï¼ˆVisitTaskCreateApi / PublicityApi / OtherVisitApi),运行时按类型查找实现,取不到就降级。
    </description>
    <dependencies>
        <!-- å®šæ—¶ä»»åŠ¡ï¼šä»»åŠ¡ç»„ã€Œè½®æ¬¡æ»šåŠ¨ã€åŽŸæœ¬æ³¨å†Œåœ¨ ruoyi-quartz çš„ RyTask é‡Œï¼Œ
             æœ¬æ¨¡å—只做实现(不注册 Job),保留 quartz ä¾èµ–以便将来把调度也收进来 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.mchange</groupId>
                    <artifactId>c3p0</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- é€šç”¨å·¥å…·ï¼š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>
    </dependencies>
</project>
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/InhospVisitModule.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package com.ruoyi.inhospvisit;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—包根。
 * <p>
 * ç›®å½•约定:
 * <ul>
 *   <li>{@code com.ruoyi.inhospvisit.api} â€”— <b>唯一对外包</b>,其它模块只能依赖这个包;</li>
 *   <li>{@code controller} / {@code service} / {@code mapper} / {@code domain} â€”— æ¨¡å—内部实现,
 *       å…¶å®ƒæ¨¡å—不应直接引用。</li>
 * </ul>
 * æ¨¡å—可通过 {@code sys_config} å‚数表的 {@code inhospvisit.enabled=false} æ•´ä½“关闭
 * ï¼ˆè¿è¡Œæ—¶è¯»å–,改完最多 10 ç§’生效);关闭后门面返回安全值并打 WARN,
 * ç›´æŽ¥è®¿é—®å‡ºå…¥é™¢éšè®¿ç«¯ç‚¹çš„请求会被拦截器拦下并返回明确的"未启用"业务错误。
 * <p>
 * <b>⚠️ å¼€å…³çš„作用范围(2025 æ›´æ­£ï¼‰</b>:
 * <ul>
 *   <li><b>HTTP è§¦å‘端点</b>:由 {@code InhospVisitAvailabilityInterceptor} æ‹¦ä¸‹ï¼ŒæŠ¥æ˜Žç¡®ä¸šåŠ¡é”™è¯¯ï¼›</li>
 *   <li><b>定时任务</b>:<b>受本开关控制</b>({@code RyTask} çš„ 5 ä¸ªå…¥å£ï¼š
 *       dealOutHospInfo / dealOutHospInfoBySchedule / dealTaskGroupOutHosp /
 *       rollTaskGroupCycle èµ°é—¨é¢ï¼ŒdealSubtaskDeadline èµ°å…¥å£å®ˆå«ï¼‰ï¼›</li>
 *   <li><b>跨模块调用</b>:门面统一判开关;</li>
 *   <li><b>基础出入院信息读写</b>({@code InhospVisitDataService}):<b>刻意不受开关管</b> â€”—
 *       å¦åˆ™å…³æŽ‰éšè®¿ä¼šè¿žå¸¦è®© HIS é‡‡é›†ä¸Žè®°å½•管理瘫痪。</li>
 * </ul>
 * <p>
 * âš ï¸ <b>本注释此前写的是"开关不作用于定时任务"</b>,理由是"关掉 = å½“天全院随访停摆,
 * è€Œ sys_config æ˜¯çƒ­è¯»çš„、一次误操作没有反馈渠道"。该说法<b>与事实不符且已更正</b>:
 * å½“时定时任务走的是 smartor é‡Œçš„遗留实现 {@code PatMedInhospServiceImpl},
 * å®ƒ<b>从不调用门面的编排方法</b>(只用门面取数据),所以真实状态是
 * "开关对定时任务无效、且没有任何提示" â€”— æ¯”"受控"更糟,因为它让人以为已经关掉了。
 * çŽ°åœ¨å®šæ—¶ä»»åŠ¡æ”¹èµ°é—¨é¢ï¼Œå…³é—­æ—¶æ—¥å¿—ä¼šæ˜Žç¡®æ‰“å‡ºã€Œæ¨¡å—æœªå¯ç”¨â€¦è·³è¿‡ã€ï¼Œ
 * å‚数在「系统管理 â†’ å‚数设置」里可见可改,反馈渠道是通畅的。
 * è¯¦è§ {@code doc/模块开关有效性核查与修复.md}。
 */
public final class InhospVisitModule {
    /**
     * æ¨¡å—配置前缀(仅作为 yml å…œåº•配置的键前缀;主开关在 sys_config å‚数表里)
     */
    public static final String PROPERTIES_PREFIX = "inhospvisit";
    /**
     * sys_config å‚数表里的开关键名
     */
    public static final String SWITCH_KEY = "inhospvisit.enabled";
    private InhospVisitModule() {
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultArchiveReadApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,38 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.common.core.service.ArchiveReadApi;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.mapper.VisitArchiveMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * {@link ArchiveReadApi} çš„实现(<b>常驻 Bean</b>)。
 *
 * <h3>为什么实现在本模块</h3>
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—自第 6 æ­¥èµ·å°±è‡ªå¸¦ {@link VisitArchiveMapper}
 * ï¼ˆåªè¯» {@code pat_archive} çš„ 10 åˆ—,与 smartor çš„
 * {@code PatArchiveMapper.selectPatArchiveByPatid} ç­‰ä»·ï¼‰ã€‚
 * æœ¬ç±»åªæ˜¯æŠŠå®ƒåŒ…成 ruoyi-common çš„契约,供其它随访模块经契约调用,
 * é¿å…æ¯ä¸ªæ¨¡å—各写一份同表只读 SQL。
 *
 * <h3>⚠️ çº¯è½¬å‘</h3>
 * ä¸åŠ å·¥æ¡ä»¶ã€ä¸è¡¥é»˜è®¤å€¼ã€‚æŸ¥è¯¢æ¡ä»¶å°±æ˜¯åŽŸå®žçŽ°çš„
 * {@code where id = #{patid} and del_flag = 0}。
 */
@Slf4j
@Component
public class DefaultArchiveReadApi implements ArchiveReadApi {
    @Autowired
    private VisitArchiveMapper visitArchiveMapper;
    @Override
    public VisitArchiveRow findByPatid(Long patid) {
        if (patid == null) {
            return null;
        }
        return visitArchiveMapper.selectByPatid(patid);
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultInhospVisitApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,192 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto;
import com.ruoyi.inhospvisit.service.InhospVisitTaskService;
import com.ruoyi.inhospvisit.service.TaskGroupCycleRoller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * {@link InhospVisitApi} çš„唯一内部实现(<b>常驻 Bean</b>)。
 *
 * <p>⚠️ <b>调用方不要直接注入本类</b>,应注入常驻门面 {@link InhospVisitFacade}
 * â€”— é—¨é¢è´Ÿè´£å¼€å…³åˆ¤æ–­ä¸Žé™çº§ï¼Œæœ¬ç±»åªåšè½¬å‘。
 *
 * <p>⚠️ <b>刻意不加 {@code @ConditionalOnProperty}</b>:该注解只在启动时求值一次,
 * æ— æ³•支持"改完 {@code sys_config} å‚数即时生效";开关判断统一由门面在运行时做。
 *
 * <h3>当前接入状态(第 10 æ­¥ï¼‰</h3>
 * <table border="1">
 *   <tr><th>方法</th><th>状态</th></tr>
 *   <tr><td>{@link #dealOutHospInfo(String)}</td>
 *       <td>✅ å·²æŽ¥å…¥ {@link InhospVisitTaskService}(第 10 æ­¥åˆ‡æ¢ï¼‰</td></tr>
 *   <tr><td>{@link #rollTaskGroupCycle()}</td>
 *       <td>✅ å·²æŽ¥å…¥ {@link TaskGroupCycleRoller}(第 9 æ­¥æ¬è¿ï¼‰</td></tr>
 *   <tr><td>{@link #dealOutHospInfoBySchedule(String)}</td>
 *       <td>⏸ æœªæŽ¥å…¥ï¼šè¯¥æ–¹æ³•本次不搬(含手术分支与任务组 builder,属第二批
 *           {@code ruoyi-othervisit} èŒƒå›´ï¼‰ï¼Œä»ç”± smartor çš„实现承接。
 *           {@code RyTask} åœ¨æœ¬æ­¥<b>不改这一处</b>。</td></tr>
 *   <tr><td>{@link #buildAgainVisitDraft}</td>
 *       <td>⏸ æœªæŽ¥å…¥ï¼šã€Œéšè®¿è¶…时→再次随访」链路;原实现调
 *           {@code boxedServiceSubtask},需把 {@code pat_med_operation_item} ç»´åº¦ä¹Ÿè€ƒè™‘进来。
 *           æœ¬æ¬¡ä¿æŒçŽ°çŠ¶ï¼ˆè¿”å›žç©ºç»“æžœ = ä¸ç”Ÿæˆï¼‰ï¼Œå¹¶åœ¨ {@code RyTask} ä¾§æš‚不改这一处。</td></tr>
 *   <tr><td>{@link #generatePublicitySubtask}</td>
 *       <td>✅ å·²æŽ¥å…¥ï¼ˆå®£æ•™æ¨¡å—回调本模块建子任务)</td></tr>
 * </table>
 */
@Slf4j
@Component
public class DefaultInhospVisitApi implements InhospVisitApi {
    @Autowired
    private InhospVisitTaskService inhospVisitTaskService;
    @Autowired
    private TaskGroupCycleRoller taskGroupCycleRoller;
    @Autowired
    private com.ruoyi.inhospvisit.service.InhospSubtaskWriter inhospSubtaskWriter;
    @Autowired
    private com.ruoyi.inhospvisit.mapper.VisitInhospMapper inhospMapper;
    @Autowired
    private com.ruoyi.inhospvisit.mapper.VisitArchiveMapper archiveMapper;
    @Autowired
    private org.springframework.beans.factory.ObjectProvider<com.ruoyi.common.core.service.OtherVisitApi> otherVisitApiProvider;
    @Autowired
    private org.springframework.beans.factory.ObjectProvider<com.ruoyi.common.core.service.OuthospVisitApi> outhospVisitApiProvider;
    private com.ruoyi.common.core.service.OtherVisitApi otherVisitApi() {
        if (otherVisitApiProvider == null) {
            return null;
        }
        try {
            return otherVisitApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 OtherVisitApi å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    private com.ruoyi.common.core.service.OuthospVisitApi outhospVisitApi() {
        if (outhospVisitApiProvider == null) {
            return null;
        }
        try {
            return outhospVisitApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 OuthospVisitApi å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    @Override
    public boolean isEnabled() {
        // é—¨é¢å·²ç»åˆ¤è¿‡å¼€å…³ï¼›è¿™é‡Œè¡¨ç¤º"实现存在"
        return true;
    }
    @Override
    public int dealOutHospInfo(String config) {
        return inhospVisitTaskService.dealOutHospInfo(config);
    }
    /**
     * âœ… å·²æŽ¥å…¥ï¼šè½¬å‘到 {@code com.ruoyi.common.core.service.OtherVisitApi}(smartor è¿‡æ¸¡å®žçŽ°ï¼‰ã€‚
     * <p>
     * è¯¥æ–¹æ³•<b>本身未搬迁</b>(含手术分支与任务组 builder,属第二批 {@code ruoyi-othervisit}),
     * ä½†ç«¯ç‚¹å·²è¿è¿›æœ¬æ¨¡å—,所以这里只做转发 â€”— è¡Œä¸ºä¸ŽåŽŸ Controller ç›´æŽ¥è°ƒ
     * {@code iPatMedInhospService.dealOutHospInfoBySchedule(config)} å®Œå…¨ä¸€è‡´ã€‚
     */
    @Override
    public int dealOutHospInfoBySchedule(String config) {
        com.ruoyi.common.core.service.OtherVisitApi api = otherVisitApi();
        if (api == null) {
            log.warn("【出入院随访】OtherVisitApi ä¸å¯ç”¨ï¼ŒdealOutHospInfoBySchedule è·³è¿‡ã€‚config={}", config);
            return 0;
        }
        return api.dealOutHospInfoBySchedule(config);
    }
    /**
     * âœ… å·²æŽ¥å…¥ï¼šè½¬å‘到 {@code com.ruoyi.common.core.service.OtherVisitApi}(smartor è¿‡æ¸¡å®žçŽ°ï¼‰ã€‚
     * <p>
     * æ‰‹æœ¯éšè®¿æœ¬æ¬¡æœªæ¬ï¼ˆå±žç¬¬äºŒæ‰¹ {@code ruoyi-othervisit}),端点已迁进本模块,故此处只转发。
     */
    @Override
    public int addOperationSubTask(String config) {
        com.ruoyi.common.core.service.OtherVisitApi api = otherVisitApi();
        if (api == null) {
            log.warn("【出入院随访】OtherVisitApi ä¸å¯ç”¨ï¼ŒaddOperationSubTask è·³è¿‡ã€‚config={}", config);
            return 0;
        }
        return api.addOperationSubTask(config);
    }
    /**
     * âœ… å·²æŽ¥å…¥ï¼šè½¬å‘到 {@code com.ruoyi.common.core.service.OuthospVisitApi}(smartor è¿‡æ¸¡å®žçŽ°ï¼‰ã€‚
     * <p>
     * é—¨è¯Šéšè®¿å±žç¬¬äºŒæ‰¹ {@code ruoyi-outpatientvisit},本次只接转发。
     * ç«™ç‚¹åˆ¤æ–­ï¼ˆ{@code active != "xh"})在 Controller é‡Œï¼Œä¸ŽåŽŸå®žçŽ°ä½ç½®ä¸€è‡´ã€‚
     */
    @Override
    public int dealOutpatientInfo() {
        com.ruoyi.common.core.service.OuthospVisitApi api = outhospVisitApi();
        if (api == null) {
            log.warn("【出入院随访】OuthospVisitApi ä¸å¯ç”¨ï¼ˆé—¨è¯Šéšè®¿æ¨¡å—未装配),dealOutpatientInfo è·³è¿‡ã€‚");
            return 0;
        }
        return api.dealOutpatientInfo();
    }
    @Override
    public int rollTaskGroupCycle() {
        return taskGroupCycleRoller.rollCycleTasks();
    }
    /**
     * â¸ æœªæŽ¥å…¥ï¼ˆè§ç±»æ³¨é‡Šï¼‰ï¼šè¿”回"未找到出入院记录",调用方会跳过该条(与原实现的 continue è¯­ä¹‰ä¸€è‡´ï¼Œ
     * ä½†ä¸ä¼šå› ä¸º"模块未接入"而误生成子任务)。
     */
    @Override
    public VisitAgainSubtaskDto buildAgainVisitDraft(String orgid, Long inhospid, Long newTaskId, Long sendstate) {
        log.warn("【出入院随访】buildAgainVisitDraft æœ¬æ¬¡æœªæŽ¥å…¥ï¼ˆéšè®¿è¶…时→再次随访链路属后续范围),"
                + "返回空结果。orgid={}, inhospid={}", orgid, inhospid);
        return InhospVisitFacade.emptyAgainDraft("本次未接入(后续范围)");
    }
    /**
     * å®£æ•™æ¨¡å—回调:为某患者生成一条宣教类子任务(type=0/4/5)。
     * <p>
     * é€è¡Œå¯¹åº”原 {@code dealOutHospInfo} é‡Œé‚£ä¸¤æ®µå®£æ•™çš„æœ€ç»ˆåŠ¨ä½œ
     * â€”— å³ {@code writeInSubTask(taskId, true, patMedInhosp, patArchive, type, config)}。
     * è¿™é‡ŒæŒ‰ inhospid é‡æ–°åŠ è½½å‡ºå…¥é™¢è®°å½•ä¸Žæ‚£è€…æ¡£æ¡ˆï¼Œç„¶åŽå§”æ‰˜ç»™
     * {@link com.ruoyi.inhospvisit.service.InhospSubtaskWriter}(<b>boxed çš„ type=0/4/5 åˆ†æ”¯å…¨ä»“只有一份</b>)。
     */
    @Override
    public boolean generatePublicitySubtask(Long taskid, Long patid, Long inhospid, int type, String config) {
        if (inhospid == null) {
            log.warn("【出入院随访】generatePublicitySubtask ç¼ºå°‘ inhospid,跳过。taskid={}, patid={}, type={}",
                    taskid, patid, type);
            return false;
        }
        com.ruoyi.inhospvisit.domain.row.VisitInhospRow inhosp = inhospMapper.selectByInhospid(inhospid);
        if (inhosp == null) {
            log.warn("【出入院随访】generatePublicitySubtask æŸ¥ä¸åˆ°å‡ºå…¥é™¢è®°å½•,跳过。inhospid={}", inhospid);
            return false;
        }
        // æ‚£è€…档案:原实现传 patMedInhosp.patid(不是宣教模块传来的 patid),这里保持一致
        com.ruoyi.common.core.service.dto.VisitArchiveRow archive = archiveMapper.selectByPatid(inhosp.getPatid());
        if (archive == null) {
            log.warn("【出入院随访】generatePublicitySubtask æŸ¥ä¸åˆ°æ‚£è€…档案,跳过。patid={}", inhosp.getPatid());
            return false;
        }
        // check=true:让 writeInSubTask å†…部做去重/再入院校验(原实现两段宣教都是 true)
        inhospSubtaskWriter.writeInSubTask(taskid, true, inhosp, archive, type, config);
        return true;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultInhospVisitDataService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,147 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.core.service.dto.PatMedRes;
import com.ruoyi.common.utils.DtoConversionUtils;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
import com.ruoyi.inhospvisit.mapper.PatMedInhospMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * {@link InhospVisitDataService} çš„唯一实现(<b>常驻 Bean</b>)。
 *
 * <p>本类是对 {@link PatMedInhospMapper} çš„<b>逐方法纯转发</b>,不含任何业务判断:
 * <ul>
 *   <li><b>不做开关判断</b> â€”— è§ {@link InhospVisitDataService} ç±»æ³¨é‡Šï¼š
 *       åŸºç¡€æ•°æ®è¯»å†™ä¸å— {@code inhospvisit.enabled} ç®¡è¾–ï¼›</li>
 *   <li><b>不补默认值/不改时间戳</b> â€”— è°ƒç”¨æ–¹åŽŸæœ¬å°±è‡ªè¡Œè®¾ç½®ï¼Œä¿æŒè¯­ä¹‰é›¶å˜åŒ–ï¼›</li>
 *   <li><b>不加 {@code @ConditionalOnProperty}</b> â€”— è¯¥æ³¨è§£åªåœ¨å¯åŠ¨æ—¶æ±‚å€¼ä¸€æ¬¡ï¼Œ
 *       ä¸Ž"改完 {@code sys_config} å³æ—¶ç”Ÿæ•ˆ"冲突(同 {@code ruoyi-satisfaction} çš„æ•™è®­ï¼‰ã€‚</li>
 * </ul>
 *
 * <p>⚠️ è°ƒç”¨æ–¹è¯·æ³¨å…¥å¸¸é©»é—¨é¢ {@link InhospVisitFacade} æˆ–本接口,
 * <b>不要</b>直接注入 {@link PatMedInhospMapper} â€”— é‚£æ ·ä¼šç»•过模块边界。
 */
@Slf4j
@Component
public class DefaultInhospVisitDataService implements InhospVisitDataService {
    @Autowired
    private PatMedInhospMapper patMedInhospMapper;
    @Override
    public PatMedInhosp selectPatMedInhospByInhospid(Long inhospid) {
        return patMedInhospMapper.selectPatMedInhospByInhospid(inhospid);
    }
    @Override
    public List<PatMedInhosp> selectPatMedInhosp(PatMedInhosp patMedInhosp) {
        return patMedInhospMapper.selectPatMedInhosp(patMedInhosp);
    }
    @Override
    public List<PatMedInhosp> selectOutInhospByPatnoAndOutDate(String patno, Date dayStart, Date dayEnd) {
        return patMedInhospMapper.selectOutInhospByPatnoAndOutDate(patno, dayStart, dayEnd);
    }
    @Override
    public String getTagnameBypatid(Long patid) {
        return patMedInhospMapper.getTagnameBypatid(patid);
    }
    @Override
    public List<Map<String, Object>> getTagnameByPatids(List<Long> patids) {
        return patMedInhospMapper.getTagnameByPatids(patids);
    }
    @Override
    public List<PatMedInhosp> selectPatMedInhospList(PatMedInhosp patMedInhosp) {
        return patMedInhospMapper.selectPatMedInhospList(patMedInhosp);
    }
    @Override
    public List<PatMedInhosp> selectPatMedInhospListBySerialnum(PatMedInhosp patMedInhosp) {
        return patMedInhospMapper.selectPatMedInhospListBySerialnum(patMedInhosp);
    }
    @Override
    public List<PatMedInhosp> selectPatMedInhospListByCondition(PatMedInhospVO patMedInhospVO) {
        return patMedInhospMapper.selectPatMedInhospListByCondition(patMedInhospVO);
    }
    @Override
    public PatMedRes selectPatMedInhospCount(PatMedReq patMedReq) {
        return patMedInhospMapper.selectPatMedInhospCount(patMedReq);
    }
    @Override
    public List<PatMedInhosp> selectNeedProcessList() {
        return patMedInhospMapper.selectNeedProcessList();
    }
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录(通过入院申请单信息)。
     *
     * <p>⚠️ <b>逐行保留原实现的缺陷</b>({@code PatMedInhospServiceImpl#selectPatMedInhospByApplication}
     * åŽŸ L196-211):第一个判断是 {@code if (!CollectionUtils.isEmpty(rows)) return null;} â€”—
     * æ¡ä»¶å†™åäº†ï¼ŒæŸ¥åˆ°è®°å½•反而返回 {@code null}。
     * <p>
     * åŽæžœï¼š<b>原实现里"è¡¥ finishFlag"的循环永远执行不到</b>,所以那个循环体依赖的
     * {@code serviceSubtaskMapper.selectServiceSubtaskList(...)}({@code service_subtask} è¡¨ï¼‰
     * åœ¨æœ¬æ¨¡å—里<b>不需要</b>存在 â€”— è¿™ä¹Ÿæ˜¯æœ¬æ–¹æ³•能干净搬进模块的原因。
     * <p>
     * è¿ç§»å·®å¼‚登记(唯一一处):原实现里那段<b>不可达</b>的循环代码未随迁,
     * æ”¹ä¸ºä¸‹é¢çš„æ³¨é‡Šè¯´æ˜Žã€‚<b>可观察行为完全一致</b>(因为它在原实现里也从不执行)。
     * è¿™æ ·æ—¢é¿å…äº†"把死代码搬进新模块",又不会让后来者以为这里本来就该没有 finishFlag é€»è¾‘。
     */
    @Override
    public List<PatMedInhospDTO> selectPatMedInhospByApplication(PatMedInhosp patMedInhosp) {
        List<PatMedInhosp> patMedInhosps = patMedInhospMapper.selectPatMedInhospList(patMedInhosp);
        if (!org.springframework.util.CollectionUtils.isEmpty(patMedInhosps)) {
            // âš ï¸ åŽŸæ ·ä¿ç•™çš„åå‘åˆ¤æ–­ï¼šæŸ¥åˆ°è®°å½•æ—¶ç›´æŽ¥è¿”å›ž null(原实现 L198-200 ä¸€æ¨¡ä¸€æ ·ï¼‰
            return null;
        }
        // åŽŸå®žçŽ°åœ¨æ­¤å¤„è¿˜æœ‰ä¸€æ®µå¾ªçŽ¯ï¼šä¸ºæ¯æ¡è®°å½•æŸ¥ service_type="4" çš„子任务以补 finishFlag。
        // ç”±äºŽä¸Šé¢é‚£ä¸ªåå‘判断,它【永远不可达】,故未随迁。
        // ä¿®ç¼ºé™·æ—¶è¦è¿žåŒå®ƒä¸€èµ·æ¢å¤ï¼Œè§ InhospVisitDataService#selectPatMedInhospByApplication çš„说明。
        return DtoConversionUtils.sourceToTarget(patMedInhosps, PatMedInhospDTO.class);
    }
    @Override
    public int insertPatMedInhosp(PatMedInhosp patMedInhosp) {
        return patMedInhospMapper.insertPatMedInhosp(patMedInhosp);
    }
    @Override
    public int insertPatMedInhospBatch(List<PatMedInhosp> list) {
        return patMedInhospMapper.insertPatMedInhospBatch(list);
    }
    @Override
    public int updatePatMedInhosp(PatMedInhosp patMedInhosp) {
        return patMedInhospMapper.updatePatMedInhosp(patMedInhosp);
    }
    @Override
    public int deletePatMedInhospByInhospid(Long inhospid) {
        return patMedInhospMapper.deletePatMedInhospByInhospid(inhospid);
    }
    @Override
    public int deletePatMedInhospByInhospno(String inhospno) {
        return patMedInhospMapper.deletePatMedInhospByInhospno(inhospno);
    }
    @Override
    public int deletePatMedInhospByInhospids(Long[] inhospids) {
        return patMedInhospMapper.deletePatMedInhospByInhospids(inhospids);
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/DefaultPreachformApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,86 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.common.core.service.PreachformApi;
import com.ruoyi.common.core.service.dto.VisitPreachformRow;
import com.ruoyi.inhospvisit.mapper.VisitPreachformMapper;
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;
/**
 * {@link PreachformApi} çš„唯一实现(<b>常驻 Bean</b>)。
 *
 * <h3>它做什么</h3>
 * æŠŠ {@code service_subtask_preachform} çš„读写收敛到本模块的
 * {@link VisitPreachformMapper},对 {@link PreachformApi} é€æ–¹æ³•纯转发。
 * <p>
 * æœ¬ç±»å­˜åœ¨çš„æ„ä¹‰æ˜¯"跨模块 SQL æ”¶å£":收口前 smartor é‡Œå¦æœ‰ä¸€æ•´ä»½åŒè¡¨çš„
 * {@code ServiceSubtaskPreachformMapper} + XML,且有 15 ä¸ªæ–‡ä»¶ç›´è¿žå®ƒ â€”—
 * åŒä¸€ä¸ªè¡¨ä¸¤å¥— CRUD,改一边就可能与另一边漂移。收口后外部只经本模块访问。
 *
 * <h3>⚠️ ä¸å«ä»»ä½•业务判断,也不判开关</h3>
 * é€æ–¹æ³•与原 Mapper å¯¹åº”,条件/列/顺序照抄。
 * "什么时候该插一条、置什么状态"属于调用方的业务。
 * å¼€å…³ï¼ˆ{@code inhospvisit.enabled})只管"要不要生成随访",不该让数据访问整体不可用。
 *
 * <h3>⚠️ ä¸‰ä¸ª softDelete* æ˜¯è½¯åˆ </h3>
 * åŽŸå®žçŽ°é‡Œé‚£ä¸‰ä¸ª {@code deleteServiceSubtaskPreachform* } çš„语句都是
 * {@code update ... set del_flag = 1},本类按实际行为命名与转发。
 */
@Slf4j
@Component
public class DefaultPreachformApi implements PreachformApi {
    @Autowired
    private VisitPreachformMapper mapper;
    @Override
    public VisitPreachformRow findById(Long id) {
        return mapper.selectById(id);
    }
    @Override
    public List<VisitPreachformRow> findList(VisitPreachformRow query) {
        List<VisitPreachformRow> rows = mapper.selectList(query);
        return rows == null ? Collections.<VisitPreachformRow>emptyList() : rows;
    }
    @Override
    public List<VisitPreachformRow> findByTaskidAndOrgid(Long taskid, String orgid) {
        List<VisitPreachformRow> rows = mapper.selectByTaskidAndOrgid(taskid, orgid);
        return rows == null ? Collections.<VisitPreachformRow>emptyList() : rows;
    }
    @Override
    public boolean insert(VisitPreachformRow row) {
        return mapper.insert(row) == 1;
    }
    @Override
    public boolean updateById(VisitPreachformRow row) {
        return mapper.updateById(row) == 1;
    }
    @Override
    public boolean updateByCondition(VisitPreachformRow row) {
        return mapper.updateByCondition(row) == 1;
    }
    @Override
    public boolean softDeleteById(Long id) {
        return mapper.softDeleteById(id) == 1;
    }
    @Override
    public boolean softDeleteByIds(Long[] ids) {
        return mapper.softDeleteByIds(ids) == 1;
    }
    @Override
    public boolean softDeleteBySubid(Long subid) {
        return mapper.softDeleteBySubid(subid) == 1;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/InhospVisitApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,120 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—的对外契约(<b>其它模块唯一允许依赖的类型</b>)。
 * <p>
 * å®žçŽ°æ˜¯æœ¬æ¨¡å—çš„å¸¸é©»é—¨é¢ {@link InhospVisitFacade}。
 *
 * <h3>约定(照抄满意度模块的 api å¥‘约纪律)</h3>
 * <ul>
 *   <li><b>只暴露 DTO ä¸ŽåŸºæœ¬ç±»åž‹</b>,不暴露本模块内部 Service/Mapper/domain;</li>
 *   <li>门面<b>永远存在</b>,所以调用方的 {@code @Autowired} ä¸ä¼šå› å¼€å…³çŠ¶æ€è€Œå¯åŠ¨å¤±è´¥ï¼›</li>
 *   <li>模块关闭 / å®žçŽ°ç¼ºå¤± / å®¹å™¨å¼‚常时,各方法返回安全空值并打 WARN,<b>不抛异常</b>
 *       â€”— å‡ºå…¥é™¢éšè®¿æŒ‚了就是当天全院随访停摆,绝不能让调用方主流程被拖垮。</li>
 * </ul>
 *
 * <h3>与 ruoyi-common é‡Œé‚£ä¸ªæŽ¥å£çš„关系</h3>
 * {@code com.ruoyi.common.core.service.PublicitySubtaskApi} æ˜¯<b>下沉给 ruoyi-publicity ç”¨çš„æœ€å°æŽ¥å£</b>
 * ï¼ˆåªæœ‰ {@code generatePublicitySubtask} ä¸€ä¸ªæ–¹æ³•)。
 * æœ¬æŽ¥å£æ˜¯<b>完整契约</b>,供直接依赖本模块的 smartor / ruoyi-quartz ä½¿ç”¨ã€‚
 * <p>
 * ä¸¤è€…<b>刻意不做继承</b>(避免"假继承"):由门面 {@link InhospVisitFacade} åŒæ—¶å®žçŽ°äºŒè€…ï¼Œ
 * äºŽæ˜¯æŒ‰ä»»ä¸€ç§ç±»åž‹æŸ¥æ‰¾éƒ½èƒ½æ‹¿åˆ°åŒä¸€ä¸ªå¸¸é©» Bean â€”— è¿™ä¸Žæ»¡æ„åº¦æŠŠ
 * {@code ISatisfactionCategoryService} ä¸€å¹¶å®žçŽ°åˆ° {@code SatisfactionFacade} æ˜¯åŒä¸€æ‰‹æ³•。
 */
public interface InhospVisitApi {
    /**
     * æ¨¡å—是否可用(开关打开 <b>且</b> å†…部实现存在)
     */
    boolean isEnabled();
    /**
     * å‡ºé™¢éšè®¿ä¸»æ‰«æï¼ˆåŽŸ {@code PatMedInhospServiceImpl#dealOutHospInfo},迁移前 L324-463)。
     * <p>
     * é¡ºåºï¼ˆè¿ç§»æ—¶å¿…须保持):
     * <ol>
     *   <li>入院宣教 â€”— æ”¹ä¸ºå›žè°ƒ {@code com.ruoyi.common.core.service.PublicityApi#dealInHospPublicity(String)};</li>
     *   <li>药品宣教(仅 sltd)—— æ”¹ä¸ºå›žè°ƒ {@code PublicityApi#dealMedicalPublicity(String)};</li>
     *   <li>基于规则的出院随访 â€”— {@code addSubTaskByRule};</li>
     *   <li>手术随访 â€”— æ”¹ä¸ºå›žè°ƒ {@code com.ruoyi.common.core.service.OtherVisitApi#addOperationSubTask(String)};</li>
     *   <li>再入院处理 â€”— {@code dealInHospInfo}。</li>
     * </ol>
     *
     * @param config {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return åŽŸå®žçŽ°å›ºå®šè¿”å›ž 1
     */
    int dealOutHospInfo(String config);
    /**
     * ä»»åŠ¡ç»„ï¼ˆappltype=5)首轮纳入(原 {@code PatMedInhospServiceImpl#dealOutHospInfoBySchedule},
     * è¿ç§»å‰ L471-614)。含手术分支(回调 {@code OtherVisitApi})。
     *
     * @param config {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return åŽŸå®žçŽ°å›ºå®šè¿”å›ž 1
     */
    int dealOutHospInfoBySchedule(String config);
    /**
     * æ‰‹æœ¯éšè®¿æ‰‹åŠ¨è§¦å‘ï¼ˆåŽŸ {@code PatMedInhospController#addOperationSubTask})。
     * <p>
     * âš ï¸ <b>本次未搬,仍由 smartor çš„实现承接</b>(属第二批 {@code ruoyi-othervisit})。
     * æœ¬æ–¹æ³•存在是为了让端点能迁进本模块(URL ä¸å˜ï¼‰ï¼Œå®žçŽ°è½¬å‘åˆ°
     * {@code com.ruoyi.common.core.service.OtherVisitApi}。
     *
     * @param config {@code sys_config: visit.early.day}
     * @return è½¬å‘结果(调用方不依赖该值)
     */
    int addOperationSubTask(String config);
    /**
     * é—¨è¯Šéšè®¿æ‰«æï¼ˆåŽŸ {@code IPatMedOuthospService#dealOutpatientInfo})。
     * <p>
     * ä¹‹æ‰€ä»¥ç”±æœ¬æ¨¡å—的入口一并触发:原 {@code /smartor/patinhosp/dealOutHospInfo} ç«¯ç‚¹
     * åœ¨åŒä¸€æ¬¡è¯·æ±‚里做了"出院随访 + é—¨è¯Šéšè®¿"两件事(L263-265),
     * ç«¯ç‚¹è¿è¿›æœ¬æ¨¡å—后必须保持这个行为。
     * <p>
     * âš ï¸ ç«™ç‚¹åˆ¤æ–­ï¼ˆ{@code active != "xh"})由调用方负责,本方法只管转发。
     *
     * @return å¤„理的门诊记录数
     */
    int dealOutpatientInfo();
    /**
     * ä»»åŠ¡ç»„å¾ªçŽ¯è½®æ¬¡æ»šåŠ¨ï¼ˆåŽŸ {@code ServiceTaskScheduleServiceImpl#rollCycleTasks},L170 èµ· 7 ä¸ªæ–¹æ³• 362 è¡Œï¼‰ã€‚
     *
     * @return æœ¬æ¬¡æŽ¨è¿›çš„计划数
     */
    int rollTaskGroupCycle();
    /**
     * ä¸ºã€Œéšè®¿è¶…æ—¶ â†’ å†æ¬¡éšè®¿ã€ç»„装子任务草稿(替代原 {@code RyTask#dealSubtaskDeadline} é‡Œçš„
     * {@code selectPatMedInhospList} + {@code boxedServiceSubtask} ä¸¤æ­¥ï¼‰ã€‚
     * <p>
     * å‡ºå…¥é™¢è®°å½•与组装逻辑搬迁后都在本模块内,所以由本方法一次完成;
     * è°ƒç”¨æ–¹æ‹¿åˆ°è‰ç¨¿åŽè‡ªå·±è¡¥ {@code upid / visitCount / visitType / visitDeptCode / visitDeptName}
     * å†è½åº“(落库走 {@code com.ruoyi.common.core.service.VisitTaskCreateApi#createSubtask})。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°åœ¨æŸ¥ä¸åˆ°å‡ºå…¥é™¢è®°å½•æ—¶æ˜¯ {@code continue} è·³è¿‡ï¼Œ
     * è°ƒç”¨æ–¹å¿…须检查 {@code isInhospFound()}。
     *
     * @param orgid     æœºæž„ID
     * @param inhospid  å‡ºå…¥é™¢è®°å½•ID
     * @param newTaskId ä¸‹ä¸€ä¸ªé•¿æœŸä»»åŠ¡ID(原 {@code serviceTask.getNexttaskid()})
     * @param sendstate è°ƒç”¨æ–¹è¦æ±‚的发送状态(原实现传 2L)
     * @return è‰ç¨¿ï¼›æ¨¡å—不可用或组装失败时返回 {@code isInhospFound()==false} çš„空结果
     */
    VisitAgainSubtaskDto buildAgainVisitDraft(String orgid, Long inhospid, Long newTaskId, Long sendstate);
    /**
     * ç»™å®£æ•™æ¨¡å—用的最小能力:生成一条宣教类子任务(type=0 è¯å“å®£æ•™ / 4 å…¥é™¢ç§‘室 / 5 å…¥é™¢ç—…区)。
     * <p>
     * ä¸Ž {@code com.ruoyi.common.core.service.PublicitySubtaskApi#generatePublicitySubtask} æ˜¯åŒä¸€èƒ½åŠ›ï¼›
     * {@link InhospVisitFacade} åŒæ—¶å®žçŽ°ä¸¤ä¸ªæŽ¥å£ï¼Œå› æ­¤æŒ‰ä»»ä¸€ç§ç±»åž‹æŸ¥æ‰¾éƒ½èƒ½æ‹¿åˆ°åŒä¸€ä¸ª Bean。
     *
     * @return true = å·²å¤„理
     */
    boolean generatePublicitySubtask(Long taskid, Long patid, Long inhospid, int type, String config);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/InhospVisitDataService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,199 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.core.service.dto.PatMedRes;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
 * å‡ºå…¥é™¢æ¨¡å—对外<b>基础出入院信息</b>数据访问契约。
 *
 * <h3>为什么单独一个契约,而不是塞进 {@link InhospVisitApi}</h3>
 * ä¸¤è€…管辖范围不同:
 * <ul>
 *   <li>{@link InhospVisitApi} â€”— <b>随访业务能力</b>(生成任务/子任务、滚动任务组周期、
 *       å®£æ•™å›žè°ƒï¼‰ã€‚受 {@code sys_config} çš„ {@code inhospvisit.enabled} å¼€å…³ç®¡è¾–,
 *       å…³é—­åŽå„方法返回安全空值。</li>
 *   <li>{@link InhospVisitDataService}(本接口)—— <b>基础出入院信息的读写</b>,
 *       å³ {@code pat_med_inhosp} è¿™ä¸€å¼ è¡¨çš„增删改查。
 *       <b>刻意不受随访开关管辖</b>:HIS é‡‡é›†ï¼ˆ{@code CollectHISServiceImpl})与
 *       æ¡£æ¡ˆæ±‡èšï¼ˆ{@code HNGatherPatArchiveServiceImpl})都依赖它写入院记录,
 *       è‹¥æŠŠå¼€å…³ä¸€å¹¶å¥—上,关闭随访生成会连带让数据采集瘫痪 â€”— é‚£æ˜¯ä¸¤ä»¶äº‹ã€‚</li>
 * </ul>
 *
 * <h3>独占边界(本次改造的目标)</h3>
 * {@code PatMedInhospMapper} ä¸Žå…¶ 1181 è¡Œ XML å·²æ¬å…¥æœ¬æ¨¡å—,
 * å…¶å®ƒæ¨¡å—<b>不得</b>再直接注入该 Mapper;一律经由本契约访问。
 * è¿™æ ·"出入院信息只有一处实现"成为编译期可验证的事实。
 *
 * <h3>⚠️ ä¸ŽåŽŸ Mapper çš„语义一致性</h3>
 * æœ¬å¥‘约的每个方法都是 {@code PatMedInhospMapper} å¯¹åº”方法的<b>纯转发</b>:
 * ä¸åŠ å·¥å‚æ•°ã€ä¸è¡¥é»˜è®¤å€¼ã€ä¸æ”¹æ—¶é—´æˆ³ï¼ˆè°ƒç”¨æ–¹åŽŸæœ¬å°±è‡ªè¡Œ {@code setCreateTime} ç­‰ï¼‰ï¼Œ
 * å› æ­¤é€æ–¹æ³•语义与搬迁前完全一致。
 */
public interface InhospVisitDataService {
    // ==================== æŸ¥è¯¢ ====================
    /**
     * æŒ‰ä¸»é”®æŸ¥è¯¢å‡ºå…¥é™¢è®°å½•。
     *
     * @param inhospid å‡ºå…¥é™¢è®°å½•主键
     * @return è®°å½•,不存在返回 {@code null}
     */
    PatMedInhosp selectPatMedInhospByInhospid(Long inhospid);
    /**
     * æŒ‰å®žä½“字段做等值条件查询。
     *
     * @param patMedInhosp æŸ¥è¯¢æ¡ä»¶ï¼ˆéž null å­—段参与过滤)
     * @return è®°å½•集合,无匹配返回空集合
     */
    List<PatMedInhosp> selectPatMedInhosp(PatMedInhosp patMedInhosp);
    /**
     * æŒ‰æ‚£è€…编号 + å‡ºé™¢æ—¥æœŸæŸ¥è¯¢å‡ºé™¢è®°å½•(出院时间只比较到天)。
     *
     * @param patno    æ‚£è€…编号
     * @param dayStart æŸ¥è¯¢æ—¥æœŸå½“天 00:00:00
     * @param dayEnd   æŸ¥è¯¢æ—¥æœŸçš„æ¬¡æ—¥ 00:00:00
     * @return å‡ºé™¢è®°å½•集合,按 inhospid å€’序
     */
    List<PatMedInhosp> selectOutInhospByPatnoAndOutDate(String patno, Date dayStart, Date dayEnd);
    /**
     * æŸ¥è¯¢æ‚£è€…标签(单患者,GROUP_CONCAT æ‹¼æŽ¥ï¼‰ã€‚
     *
     * @param patid æ‚£è€…档案id
     * @return æ ‡ç­¾ä¸²ï¼Œæ— æ ‡ç­¾è¿”回 {@code null}
     */
    String getTagnameBypatid(Long patid);
    /**
     * æ‰¹é‡æŸ¥è¯¢æ‚£è€…标签。
     *
     * @param patids æ‚£è€…档案id集合,调用方须保证非空且已去重
     * @return æ¯è¡Œå« {@code patid}、{@code tagname} ä¸¤ä¸ª key
     */
    List<Map<String, Object>> getTagnameByPatids(List<Long> patids);
    /**
     * é€šç”¨åˆ—表查询(最常用入口)。
     *
     * @param patMedInhosp æŸ¥è¯¢æ¡ä»¶
     * @return è®°å½•集合
     */
    List<PatMedInhosp> selectPatMedInhospList(PatMedInhosp patMedInhosp);
    /**
     * æŒ‰ä½é™¢æµæ°´å·æŸ¥è¯¢ã€‚
     *
     * @param patMedInhosp æŸ¥è¯¢æ¡ä»¶ï¼ˆå« serialnum)
     * @return è®°å½•集合
     */
    List<PatMedInhosp> selectPatMedInhospListBySerialnum(PatMedInhosp patMedInhosp);
    /**
     * æŒ‰ VO æ¡ä»¶æŸ¥è¯¢ï¼ˆæ”¯æŒå¤šç§‘室/多病区集合条件)。
     *
     * @param patMedInhospVO æŸ¥è¯¢æ¡ä»¶
     * @return è®°å½•集合
     */
    List<PatMedInhosp> selectPatMedInhospListByCondition(PatMedInhospVO patMedInhospVO);
    /**
     * å‡ºé™¢/入院人数与随访人次统计(首页 ECharts ç”¨ï¼‰ã€‚
     *
     * @param patMedReq ç»Ÿè®¡æ¡ä»¶
     * @return ç»Ÿè®¡ç»“æžœ
     */
    PatMedRes selectPatMedInhospCount(PatMedReq patMedReq);
    /**
     * æŸ¥è¯¢ä¸‰ä¸ªç»´åº¦é‡Œä»»æ„ä¸€ä¸ªä»æœªå¤„理的出院患者。
     *
     * @return å¾…处理记录集合
     */
    List<PatMedInhosp> selectNeedProcessList();
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录(通过入院申请单信息),返回 DTO åˆ—表。
     * <p>
     * âš ï¸ <b>本方法原样保留了一个原代码缺陷</b>,未做修正:
     * <pre>
     * List&lt;PatMedInhosp&gt; rows = selectPatMedInhospList(query);
     * if (!CollectionUtils.isEmpty(rows)) {
     *     return null;          // â† æŸ¥åˆ°è®°å½•反而返回 null,条件写反了
     * }
     * </pre>
     * åŽæžœï¼šæŸ¥åˆ°è®°å½•时恒返回 {@code null};查不到记录时返回空列表。
     * ä¹Ÿå°±æ˜¯è¯´<b>原实现里那段"给每条记录补 finishFlag(是否宣教过)"的循环永远不会执行</b>,
     * å› æ­¤æœ¬æ–¹æ³•不依赖 {@code service_subtask}(随访子任务表)。
     * <p>
     * è‹¥å°†æ¥è¦ä¿®è¿™ä¸ªç¼ºé™·ï¼Œé™¤äº†æŠŠæ¡ä»¶æ”¹æˆ {@code isEmpty},还需要恢复那段补
     * {@code finishFlag} çš„逻辑(判据:该患者是否存在 {@code service_type="4"} çš„子任务),
     * å±Šæ—¶éœ€è¦ç»™æœ¬æ¨¡å—补一个查询子任务的能力。
     * <p>
     * å‰ç«¯å½“前<b>没有</b>调用该端点(已核对 {@code smartor-web/src})。
     *
     * @param patMedInhosp æŸ¥è¯¢æ¡ä»¶
     * @return è§ä¸Šï¼›ç¼ºé™·ä¿ç•™ï¼Œæ•…查到记录时为 {@code null}
     */
    List<PatMedInhospDTO> selectPatMedInhospByApplication(PatMedInhosp patMedInhosp);
    // ==================== å†™å…¥ ====================
    /**
     * æ–°å¢žå‡ºå…¥é™¢è®°å½•。
     *
     * @param patMedInhosp è®°å½•({@code createTime}/{@code updateTime} ç”±è°ƒç”¨æ–¹è®¾ç½®ï¼Œ
     *                     æœ¬æ–¹æ³•不代填 â€”— ä¸Žæ¬è¿å‰ä¸€è‡´ï¼‰
     * @return å½±å“è¡Œæ•°
     */
    int insertPatMedInhosp(PatMedInhosp patMedInhosp);
    /**
     * æ‰¹é‡æ–°å¢žå‡ºå…¥é™¢è®°å½•。
     *
     * @param list è®°å½•集合
     * @return å½±å“è¡Œæ•°
     */
    int insertPatMedInhospBatch(List<PatMedInhosp> list);
    /**
     * ä¿®æ”¹å‡ºå…¥é™¢è®°å½•("传什么更新什么")。
     *
     * @param patMedInhosp è®°å½•(须含主键)
     * @return å½±å“è¡Œæ•°
     */
    int updatePatMedInhosp(PatMedInhosp patMedInhosp);
    /**
     * æŒ‰ä¸»é”®åˆ é™¤ã€‚
     *
     * @param inhospid ä¸»é”®
     * @return å½±å“è¡Œæ•°
     */
    int deletePatMedInhospByInhospid(Long inhospid);
    /**
     * æŒ‰ä½é™¢ç¼–码删除。
     *
     * @param inhospno ä½é™¢ç¼–码
     * @return å½±å“è¡Œæ•°
     */
    int deletePatMedInhospByInhospno(String inhospno);
    /**
     * æŒ‰ä¸»é”®é›†åˆæ‰¹é‡åˆ é™¤ã€‚
     *
     * @param inhospids ä¸»é”®é›†åˆ
     * @return å½±å“è¡Œæ•°
     */
    int deletePatMedInhospByInhospids(Long[] inhospids);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/api/InhospVisitFacade.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,550 @@
package com.ruoyi.inhospvisit.api;
import com.ruoyi.inhospvisit.config.InhospVisitProperties;
import com.ruoyi.inhospvisit.config.InhospVisitSwitch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—对外门面(<b>常驻 Bean</b>)。
 * <p>
 * è¿™æ˜¯å…¶å®ƒæ¨¡å—访问出入院随访能力的<b>唯一入口</b>。
 *
 * <h3>与内部实现的关系(照抄满意度模块,5 ä¸ªå‘全部规避)</h3>
 * <ul>
 *   <li>实现类 {@link DefaultInhospVisitApi} <b>常驻</b>(不用 {@code @ConditionalOnProperty}
 *       â€”— é‚£ä¸ªåªåœ¨å¯åŠ¨æ—¶æ±‚å€¼ä¸€æ¬¡ï¼Œä¸Ž"改完参数即时生效"冲突);</li>
 *   <li>开关在 {@code sys_config} çš„ {@code inhospvisit.enabled},由
 *       {@link InhospVisitSwitch} <b>运行时</b>读取(10 ç§’进程内缓存),改完无需重启;</li>
 *   <li>本门面<b>永远</b>是 Spring Bean,因此调用方({@code RyTask} å®šæ—¶ä»»åŠ¡ã€
 *       {@code PatMedInhospController})的 {@code @Autowired} åœ¨ä»»ä½•开关状态下都<b>不会启动失败</b>;</li>
 *   <li>关闭时各方法返回安全空值并打 WARN,<b>不抛异常</b>,保证调用方主流程照常完成。</li>
 * </ul>
 *
 * <h3>⚠️ æœ¬é—¨é¢åŒæ—¶å®žçŽ°ä¸‰ä¸ªæŽ¥å£</h3>
 * <ul>
 *   <li>{@link InhospVisitApi} â€”— æœ¬æ¨¡å—çš„<b>随访业务能力契约</b>(smartor / ruoyi-quartz ç”¨ï¼‰ï¼Œ
 *       å— {@code inhospvisit.enabled} å¼€å…³ç®¡è¾–ï¼›</li>
 *   <li>{@link InhospVisitDataService} â€”— <b>基础出入院信息读写契约</b>(本次新增)。
 *       {@code PatMedInhospMapper} å·²ç‹¬å æ¬å…¥æœ¬æ¨¡å—,其它模块一律经此访问;
 *       <b>刻意不受开关管辖</b>,否则关闭随访会连带让 HIS é‡‡é›†ç˜«ç—ªï¼›</li>
 *   <li>{@code com.ruoyi.common.core.service.PublicitySubtaskApi} â€”— <b>下沉给宣教模块的最小面</b>。
 *       å› ä¸º ruoyi-publicity ä¸èƒ½ä¾èµ–本模块(否则双向依赖),只能按 ruoyi-common é‡Œçš„æŽ¥å£æŸ¥æ‰¾ã€‚</li>
 * </ul>
 *
 * <h3>使用示例</h3>
 * <pre>
 * &#64;Autowired
 * private InhospVisitFacade inhospVisitFacade;
 *
 * if (inhospVisitFacade.isEnabled()) {
 *     inhospVisitFacade.dealOutHospInfo(config);
 * }
 *
 * // åŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯ï¼šä¸éœ€è¦åˆ¤å¼€å…³
 * List&lt;PatMedInhosp&gt; rows = inhospVisitFacade.selectPatMedInhospList(query);
 * </pre>
 */
@Slf4j
@Component
public class InhospVisitFacade implements InhospVisitApi,
        InhospVisitDataService,
        com.ruoyi.common.core.service.PreachformApi,
        com.ruoyi.common.core.service.PublicitySubtaskApi {
    private final InhospVisitProperties properties;
    /**
     * å®£æ•™å•({@code service_subtask_preachform})数据访问。
     * <p>
     * âš ï¸ ç”¨ {@code ObjectProvider} æƒ°æ€§å–,而不是直接注入接口 â€”— ç†ç”±åŒ {@code dataService}:
     * æœ¬ç±»è‡ªèº«ä¹Ÿå®žçŽ°äº† {@code PreachformApi},按接口类型直接注入会命中"正在创建的自己"
     * ï¼ˆ{@code BeanCurrentlyInCreationException})。
     * <p>
     * è¯¥è¡¨çš„归属模块是本模块;跨模块 SQL æ”¶å£åŽï¼Œå¤–部只经 {@code PreachformApi} è®¿é—®å®ƒ
     * ï¼ˆæ­¤å‰ smartor é‡Œå¦æœ‰ä¸€æ•´ä»½åŒè¡¨ Mapper ä¸Ž XML,已删除)。
     */
    private final ObjectProvider<com.ruoyi.common.core.service.PreachformApi> preachformApiProvider;
    /**
     * è¿è¡Œæ—¶å¼€å…³ï¼ˆ{@code sys_config} ä¼˜å…ˆï¼Œ{@link InhospVisitProperties} å…œåº•)
     */
    private final InhospVisitSwitch inhospVisitSwitch;
    /**
     * å†…部实现;模块关闭时为 {@code null}。
     * <p>
     * ç”¨ {@code ObjectProvider} æƒ°æ€§èŽ·å–ï¼šå³ä½¿å®žçŽ° Bean ä¸å­˜åœ¨ä¹Ÿä¸ä¼šå¯¼è‡´æ³¨å…¥å¤±è´¥ã€‚
     */
    private final ObjectProvider<InhospVisitApi> apiProvider;
    /**
     * åŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯æ•°æ®è®¿é—®ï¼ˆ<b>必存在</b>,不受开关管辖)。
     * <p>
     * âš ï¸ <b>用 {@link ObjectProvider} æƒ°æ€§èŽ·å–ï¼Œå¹¶åœ¨è§£æžæ—¶è¿‡æ»¤æŽ‰"自己"</b>:
     * æœ¬ç±»è‡ªèº«ä¹Ÿå®žçŽ°äº† {@link InhospVisitDataService},若直接注入接口类型,
     * Spring è§£æžå€™é€‰æ—¶ä¼šå‘½ä¸­<b>正在创建的自己</b>,抛
     * {@code BeanCurrentlyInCreationException}(实测确认)。
     * è¯¦è§ {@link #firstDataServiceOrNull(ObjectProvider)}。
     * <p>
     * ç”¨ {@code ObjectProvider} è€Œéžç›´æŽ¥æ³¨å…¥è¿˜å¸¦æ¥è£…配健壮性:容器里没有该实现
     * ï¼ˆä¾‹å¦‚不起数据库的测试上下文)时,门面依然能装配,数据方法走降级分支,
     * è€Œä¸ä¼šè®©æ•´ä¸ªåº”用启动失败。
     */
    private final InhospVisitDataService dataService;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器
     * ï¼ˆå¦ä¸€ä¸ªæ˜¯ç»™æµ‹è¯•/非 Spring çŽ¯å¢ƒç”¨çš„ï¼‰ï¼Œè‹¥ä¸æ ‡æ³¨ï¼ŒSpring ä¼šå› ä¸º"构造器不唯一"
     * è€Œé€€å›žæŸ¥æ‰¾æ— å‚构造器,从而在启动时报 {@code No default constructor found}。
     * â€”— è¿™æ˜¯æ»¡æ„åº¦æ¨¡å—<b>实机踩到</b>的缺陷,此处刻意保留同样的写法与注释。
     */
    @Autowired
    public InhospVisitFacade(InhospVisitProperties properties,
                             InhospVisitSwitch inhospVisitSwitch,
                             ObjectProvider<InhospVisitApi> apiProvider,
                             ObjectProvider<InhospVisitDataService> dataServiceProvider,
                             ObjectProvider<com.ruoyi.common.core.service.PreachformApi> preachformApiProvider) {
        this.properties = properties;
        this.inhospVisitSwitch = inhospVisitSwitch;
        this.apiProvider = apiProvider;
        this.dataService = firstDataServiceOrNull(dataServiceProvider);
        this.preachformApiProvider = preachformApiProvider;
    }
    /**
     * å–宣教单数据访问实现;容器里没有、状态异常、或<b>只找到自己</b>时返回 {@code null}。
     * <p>
     * âš ï¸ ä¸Ž {@link #firstDataServiceOrNull} åŒä¸€æ‰‹æ³•与理由(自引用 / å¾ªçŽ¯ä¾èµ–ï¼‰ã€‚
     */
    private com.ruoyi.common.core.service.PreachformApi preachformOrNull() {
        if (preachformApiProvider == null) {
            return null;
        }
        try {
            return preachformApiProvider.stream()
                    .filter(svc -> svc != this)
                    .findFirst()
                    .orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取宣教单数据服务失败,相关访问将降级:{}", e.toString());
            return null;
        }
    }
    /**
     * æ•°æ®æœåŠ¡æ˜¯å¦ä¸å¯ç”¨ï¼ˆä¸å¯ç”¨æ—¶æ‰“ WARN å¹¶è¿”回 true)
     */
    private boolean preachformUnavailable(String method) {
        if (preachformOrNull() == null) {
            log.warn("【出入院随访】宣教单数据服务不可用,{} è·³è¿‡ã€‚", method);
            return true;
        }
        return false;
    }
    // ==================================================================
    // å®£æ•™å• service_subtask_preachform(PreachformApi)
    //
    // âš ï¸ ä¸Žä¸Šé¢ 6 ä¸ªéšè®¿æ–¹æ³•的关键区别:以下方法【不做开关判断】。
    //    å®ƒæ˜¯è·¨æ¨¡å— SQL æ”¶å£åŽçš„æ•°æ®è®¿é—®å…¥å£ï¼ˆæ›¿ä»£ smartor é‡Œé‚£ä»½åŒè¡¨ Mapper),
    //    å±žæ•°æ®åŸºç¡€è®¾æ–½ï¼›å¼€å…³åªè¯¥æŽ§åˆ¶"要不要生成随访"。
    // ==================================================================
    @Override
    public com.ruoyi.common.core.service.dto.VisitPreachformRow findById(Long id) {
        return preachformUnavailable("findById") ? null : preachformOrNull().findById(id);
    }
    @Override
    public java.util.List<com.ruoyi.common.core.service.dto.VisitPreachformRow> findList(
            com.ruoyi.common.core.service.dto.VisitPreachformRow query) {
        return preachformUnavailable("findList")
                ? java.util.Collections.<com.ruoyi.common.core.service.dto.VisitPreachformRow>emptyList()
                : preachformOrNull().findList(query);
    }
    @Override
    public java.util.List<com.ruoyi.common.core.service.dto.VisitPreachformRow> findByTaskidAndOrgid(
            Long taskid, String orgid) {
        return preachformUnavailable("findByTaskidAndOrgid")
                ? java.util.Collections.<com.ruoyi.common.core.service.dto.VisitPreachformRow>emptyList()
                : preachformOrNull().findByTaskidAndOrgid(taskid, orgid);
    }
    @Override
    public boolean insert(com.ruoyi.common.core.service.dto.VisitPreachformRow row) {
        return !preachformUnavailable("insert") && preachformOrNull().insert(row);
    }
    @Override
    public boolean updateById(com.ruoyi.common.core.service.dto.VisitPreachformRow row) {
        return !preachformUnavailable("updateById") && preachformOrNull().updateById(row);
    }
    @Override
    public boolean updateByCondition(com.ruoyi.common.core.service.dto.VisitPreachformRow row) {
        return !preachformUnavailable("updateByCondition") && preachformOrNull().updateByCondition(row);
    }
    @Override
    public boolean softDeleteById(Long id) {
        return !preachformUnavailable("softDeleteById") && preachformOrNull().softDeleteById(id);
    }
    @Override
    public boolean softDeleteByIds(Long[] ids) {
        return !preachformUnavailable("softDeleteByIds") && preachformOrNull().softDeleteByIds(ids);
    }
    @Override
    public boolean softDeleteBySubid(Long subid) {
        return !preachformUnavailable("softDeleteBySubid") && preachformOrNull().softDeleteBySubid(subid);
    }
    /**
     * å–数据服务实现;容器里没有、状态异常、或<b>只找到自己</b>时返回 {@code null}。
     * <p>
     * âš ï¸ <b>必须过滤掉"自己"</b>,两个理由:
     * <ol>
     *   <li><b>自引用</b>:本类自身也实现了 {@link InhospVisitDataService}。若不过滤,
     *       å®¹å™¨åœ¨ç¬¬ä¸€è½®æ‰¾ä¸åˆ°åˆ«çš„实现时会把门面自己当候选返回,随后
     *       {@code dataService.selectPatMedInhospList(...)} å˜æˆæ— é™é€’å½’
     *       ï¼ˆ{@code StackOverflowError})。这是满意度模块<b>实机踩到</b>的同类缺陷。</li>
     *   <li><b>循环依赖</b>:不过滤时 Spring ä¼šåœ¨æž„造本 Bean çš„过程中解析到"正在创建的自己",
     *       ç›´æŽ¥æŠ› {@code BeanCurrentlyInCreationException}。实测确认过这一点。</li>
     * </ol>
     * <p>
     * ç”¨ {@code stream()} è€Œä¸æ˜¯ {@code getIfAvailable()}:后者在候选多于一个时抛
     * {@code NoUniqueBeanDefinitionException},会被吞成 null å¹¶æŽ©ç›–"有人又加了一个实现"。
     */
    private InhospVisitDataService firstDataServiceOrNull(
            ObjectProvider<InhospVisitDataService> provider) {
        if (provider == null) {
            return null;
        }
        try {
            return provider.stream()
                    .filter(svc -> svc != this)
                    .findFirst()
                    .orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取数据服务失败,基础出入院信息访问将降级:{}", e.toString());
            return null;
        }
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造(直接给出内部实现,可为 null)。
     * <p>
     * å¼€å…³å–自 {@code sys_config} çš„æ¨¡æ‹Ÿé”®å€¼ï¼Œä»Žè€Œä¿è¯"开关校验"脚本无需 Spring/数据库即可运行。
     * <p>
     * æ•°æ®æœåŠ¡ä¸ä¼ ï¼šæµ‹è¯•æž„é€ ä¸‹ä¸æä¾›åŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯è®¿é—®ä¸Žå®£æ•™å•è®¿é—®
     * ï¼ˆè°ƒç”¨ä¼šå¾—到 null / ç©ºé›†åˆ / false),保证"开关校验"类测试不必拉起数据库。
     */
    public InhospVisitFacade(InhospVisitProperties properties, InhospVisitApi delegate, String sysConfigValue) {
        this(properties, new InhospVisitSwitch(properties, sysConfigValue), new FixedProvider(delegate),
                null, null);
    }
    /**
     * å–运行时开关(供拦截器复用同一个实例、也供测试断言)
     */
    public InhospVisitSwitch getInhospVisitSwitch() {
        return inhospVisitSwitch;
    }
    /**
     * å–内部实现;模块关闭、实现缺失、或只找到自己时返回 {@code null}。
     * <p>
     * âš ï¸ <b>必须排除"自己"</b>:本类自身也实现了 {@link InhospVisitApi},而 Spring åœ¨
     * æŒ‰ç±»åž‹æŸ¥æ‰¾å€™é€‰ Bean æ—¶ï¼Œè‹¥ç¬¬ä¸€è½®æ²¡æ‰¾åˆ°åˆ«çš„实现(例如 ruoyi-inhospvisit
     * æ²¡æœ‰è¢«æ‰“进应用,或将来又有人用条件装配把它摘掉),会<b>把自引用当作兜底候选</b>返回。
     * é‚£æ · {@code delegateOrNull()} ä¼šæ‹¿åˆ° {@code this},随后
     * {@code delegate.dealOutHospInfo(...)} å°±å˜æˆæ— é™é€’归({@code StackOverflowError})。
     * è¿™é‡Œæ˜¾å¼è¿‡æ»¤æŽ‰è‡ªå·±ï¼ŒæŠŠ"没有实现"如实视为不可用。
     * â€”— è¿™æ˜¯æ»¡æ„åº¦æ¨¡å—<b>实机踩到</b>的缺陷。
     * <p>
     * âš ï¸ <b>本方法不能替代开关判断</b>:开关关闭时 {@link DefaultInhospVisitApi} ä¾ç„¶å­˜åœ¨ï¼Œ
     * æ‰€ä»¥è°ƒç”¨ç‚¹ä¸€å¾‹å…ˆåˆ¤ {@link #isEnabled()}。
     * <p>
     * ç”¨ {@code stream()} è€Œä¸æ˜¯ {@code getIfAvailable()}:后者在候选多于一个时抛
     * {@code NoUniqueBeanDefinitionException}(它继承自 {@code NoSuchBeanDefinitionException}),
     * æœ€ç»ˆä¼šè¢«åžæˆ null,掩盖"有人又加了一个实现"这种配置问题。
     */
    private InhospVisitApi delegateOrNull() {
        if (apiProvider == null) {
            return null;
        }
        try {
            return apiProvider.stream()
                    .filter(api -> api != this)
                    .findFirst()
                    .orElse(null);
        } catch (Exception e) {
            // å®¹å™¨çŠ¶æ€å¼‚å¸¸æ—¶æŒ‰"未启用"处理,绝不让调用方(定时任务、Controller)因为随访模块而失败
            log.warn("【出入院随访】获取内部实现失败,按未启用处理:{}", e.toString());
            return null;
        }
    }
    @Override
    public boolean isEnabled() {
        return inhospVisitSwitch.isEnabled() && delegateOrNull() != null;
    }
    @Override
    public int dealOutHospInfo(String config) {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),dealOutHospInfo è·³è¿‡ã€‚config={}", config);
            return 0;
        }
        return delegateOrNull().dealOutHospInfo(config);
    }
    @Override
    public int dealOutHospInfoBySchedule(String config) {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),dealOutHospInfoBySchedule è·³è¿‡ã€‚config={}", config);
            return 0;
        }
        return delegateOrNull().dealOutHospInfoBySchedule(config);
    }
    @Override
    public int addOperationSubTask(String config) {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),addOperationSubTask è·³è¿‡ã€‚config={}", config);
            return 0;
        }
        return delegateOrNull().addOperationSubTask(config);
    }
    @Override
    public int dealOutpatientInfo() {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),dealOutpatientInfo è·³è¿‡ã€‚");
            return 0;
        }
        return delegateOrNull().dealOutpatientInfo();
    }
    @Override
    public int rollTaskGroupCycle() {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),rollTaskGroupCycle è·³è¿‡ã€‚");
            return 0;
        }
        return delegateOrNull().rollTaskGroupCycle();
    }
    @Override
    public com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto buildAgainVisitDraft(String orgid, Long inhospid,
                                                                                      Long newTaskId, Long sendstate) {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),buildAgainVisitDraft è¿”回空结果。"
                    + "orgid={}, inhospid={}", orgid, inhospid);
            return emptyAgainDraft("模块未启用");
        }
        return delegateOrNull().buildAgainVisitDraft(orgid, inhospid, newTaskId, sendstate);
    }
    @Override
    public boolean generatePublicitySubtask(Long taskid, Long patid, Long inhospid, int type, String config) {
        if (!isEnabled()) {
            log.warn("【出入院随访】模块未启用(参数配置 inhospvisit.enabled=false),generatePublicitySubtask è·³è¿‡ã€‚"
                    + "taskid={}, patid={}, type={}", taskid, patid, type);
            return false;
        }
        return delegateOrNull().generatePublicitySubtask(taskid, patid, inhospid, type, config);
    }
    // ==================================================================
    // åŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯è¯»å†™ï¼ˆInhospVisitDataService)
    //
    // âš ï¸ ä¸Žä¸Šé¢ 6 ä¸ªéšè®¿æ–¹æ³•的关键区别:以下方法【不做开关判断】。
    //    ç†ç”±è§ InhospVisitDataService ç±»æ³¨é‡Š â€”— å…³é—­éšè®¿ç”Ÿæˆä¸è¯¥è®©
    //    HIS é‡‡é›†/档案汇聚无法写入院记录。
    //
    // âš ï¸ æ¯ä¸ªæ–¹æ³•都对 dataService åˆ¤ç©ºï¼šåªæœ‰åœ¨"测试构造器"下它才是 null;
    //    Spring çŽ¯å¢ƒä¸‹ç”± @Autowired ä¿è¯éž null。
    //    è¿™é‡Œè¿”回空值而非抛异常,保证调用方主流程不因本模块而失败。
    // ==================================================================
    private boolean dataUnavailable(String method) {
        if (dataService == null) {
            log.warn("【出入院随访】数据服务不可用(非 Spring æž„造或装配缺失),{} è·³è¿‡ã€‚", method);
            return true;
        }
        return false;
    }
    @Override
    public com.ruoyi.inhospvisit.domain.PatMedInhosp selectPatMedInhospByInhospid(Long inhospid) {
        return dataUnavailable("selectPatMedInhospByInhospid") ? null
                : dataService.selectPatMedInhospByInhospid(inhospid);
    }
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhosp(
            com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
        return dataUnavailable("selectPatMedInhosp") ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhosp>emptyList()
                : dataService.selectPatMedInhosp(patMedInhosp);
    }
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectOutInhospByPatnoAndOutDate(
            String patno, java.util.Date dayStart, java.util.Date dayEnd) {
        return dataUnavailable("selectOutInhospByPatnoAndOutDate")
                ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhosp>emptyList()
                : dataService.selectOutInhospByPatnoAndOutDate(patno, dayStart, dayEnd);
    }
    @Override
    public String getTagnameBypatid(Long patid) {
        return dataUnavailable("getTagnameBypatid") ? null : dataService.getTagnameBypatid(patid);
    }
    @Override
    public java.util.List<java.util.Map<String, Object>> getTagnameByPatids(java.util.List<Long> patids) {
        return dataUnavailable("getTagnameByPatids")
                ? java.util.Collections.<java.util.Map<String, Object>>emptyList()
                : dataService.getTagnameByPatids(patids);
    }
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhospList(
            com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
        return dataUnavailable("selectPatMedInhospList")
                ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhosp>emptyList()
                : dataService.selectPatMedInhospList(patMedInhosp);
    }
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhospListBySerialnum(
            com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
        return dataUnavailable("selectPatMedInhospListBySerialnum")
                ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhosp>emptyList()
                : dataService.selectPatMedInhospListBySerialnum(patMedInhosp);
    }
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhospListByCondition(
            com.ruoyi.inhospvisit.domain.PatMedInhospVO patMedInhospVO) {
        return dataUnavailable("selectPatMedInhospListByCondition")
                ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhosp>emptyList()
                : dataService.selectPatMedInhospListByCondition(patMedInhospVO);
    }
    @Override
    public com.ruoyi.common.core.service.dto.PatMedRes selectPatMedInhospCount(
            com.ruoyi.common.core.service.dto.PatMedReq patMedReq) {
        return dataUnavailable("selectPatMedInhospCount") ? null : dataService.selectPatMedInhospCount(patMedReq);
    }
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectNeedProcessList() {
        return dataUnavailable("selectNeedProcessList")
                ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhosp>emptyList()
                : dataService.selectNeedProcessList();
    }
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录(通过入院申请单信息)。
     * <p>
     * âš ï¸ é™çº§æ—¶è¿”回<b>空列表</b>(不是 null)—— è°ƒç”¨æ–¹çš„分页逻辑对 null ä¸Žç©ºåˆ—表处理相同,
     * ä½†ç©ºåˆ—表能避免调用方额外判空。注意本方法在<b>正常</b>路径下也可能返回 {@code null}
     * ï¼ˆåŽŸå®žçŽ°çš„åå‘åˆ¤æ–­ç¼ºé™·ï¼Œè§ {@code InhospVisitDataService} çš„说明),那是业务行为,不是降级。
     */
    @Override
    public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhospDTO> selectPatMedInhospByApplication(
            com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
        return dataUnavailable("selectPatMedInhospByApplication")
                ? java.util.Collections.<com.ruoyi.inhospvisit.domain.PatMedInhospDTO>emptyList()
                : dataService.selectPatMedInhospByApplication(patMedInhosp);
    }
    @Override
    public int insertPatMedInhosp(com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
        return dataUnavailable("insertPatMedInhosp") ? 0 : dataService.insertPatMedInhosp(patMedInhosp);
    }
    @Override
    public int insertPatMedInhospBatch(java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> list) {
        return dataUnavailable("insertPatMedInhospBatch") ? 0 : dataService.insertPatMedInhospBatch(list);
    }
    @Override
    public int updatePatMedInhosp(com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
        return dataUnavailable("updatePatMedInhosp") ? 0 : dataService.updatePatMedInhosp(patMedInhosp);
    }
    @Override
    public int deletePatMedInhospByInhospid(Long inhospid) {
        return dataUnavailable("deletePatMedInhospByInhospid") ? 0 : dataService.deletePatMedInhospByInhospid(inhospid);
    }
    @Override
    public int deletePatMedInhospByInhospno(String inhospno) {
        return dataUnavailable("deletePatMedInhospByInhospno") ? 0 : dataService.deletePatMedInhospByInhospno(inhospno);
    }
    @Override
    public int deletePatMedInhospByInhospids(Long[] inhospids) {
        return dataUnavailable("deletePatMedInhospByInhospids") ? 0
                : dataService.deletePatMedInhospByInhospids(inhospids);
    }
    /**
     * ç©ºç»“果(键与"启用但组装失败"时一致,保证调用方不会 NPE)
     */
    public static com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto emptyAgainDraft(String reason) {
        com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto dto =
                new com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto();
        dto.setDraft(null);
        dto.setInhospFound(false);
        dto.setMissReason(reason);
        return dto;
    }
    /**
     * å›ºå®šçš„ {@code ObjectProvider} å®žçŽ°ï¼šä»…ç”¨äºŽéž Spring çŽ¯å¢ƒ/测试。
     */
    private static final class FixedProvider implements ObjectProvider<InhospVisitApi> {
        private final InhospVisitApi api;
        private FixedProvider(InhospVisitApi api) {
            this.api = api;
        }
        @Override
        public InhospVisitApi getObject(Object... args) {
            return api;
        }
        @Override
        public InhospVisitApi getIfAvailable() {
            return api;
        }
        @Override
        public InhospVisitApi getIfUnique() {
            return api;
        }
        @Override
        public InhospVisitApi getObject() {
            return api;
        }
        @Override
        public java.util.stream.Stream<InhospVisitApi> stream() {
            return api == null ? java.util.stream.Stream.empty() : java.util.stream.Stream.of(api);
        }
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitAvailabilityInterceptor.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package com.ruoyi.inhospvisit.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 inhospvisit.enabled=false})时,
 * å‘½ä¸­å‡ºå…¥é™¢éšè®¿ç«¯ç‚¹çš„请求会被立即拦下并抛出 {@link ServiceException},
 * ç”± RuoYi çš„全局异常处理器返回<b>明确的业务错误提示</b>(而不是 404 æˆ–静默返回空数据)。
 * <p>
 * å¼€å…³å€¼ç”± {@link InhospVisitSwitch} åœ¨è¿è¡Œæ—¶è¯»å–({@code sys_config},10 ç§’进程内缓存),
 * å› æ­¤æ”¹å®Œå‚数设置<b>不需要重启</b>,最多 10 ç§’即生效。
 *
 * <h3>⚠️ æœ¬æ‹¦æˆªå™¨<b>不覆盖定时任务</b></h3>
 * {@code RyTask.dealOutHospInfo} ç­‰å®šæ—¶å…¥å£ä¸èµ° MVC,因此不受本开关影响 â€”— è¿™æ˜¯åˆ»æ„çš„:
 * å…³æŽ‰å®šæ—¶ä»»åŠ¡ = å½“天全院随访停摆,而开关是热读的,一次误操作毫无反馈渠道。
 * è¦åœå®šæ—¶ä»»åŠ¡è¯·åˆ°ã€Œç³»ç»Ÿç®¡ç† â†’ å®šæ—¶ä»»åŠ¡ã€æš‚åœå¯¹åº”çš„ {@code sys_job}。
 *
 * <h3>为什么不用 {@code @ConditionalOnProperty} è®© Controller ç›´æŽ¥ä¸æ³¨å†Œ</h3>
 * <ul>
 *   <li>那样调用方只会拿到 HTTP 404 â€”— å‰ç«¯/运维无法区分"模块被关闭"和"地址写错了";</li>
 *   <li>而且 {@code @ConditionalOnProperty} åªåœ¨<b>启动时</b>求值一次,
 *       åšä¸åˆ°"改完参数配置就生效",与参数表开关的诉求冲突。</li>
 * </ul>
 *
 * <h3>为什么不会影响其它业务与启动</h3>
 * <ul>
 *   <li>本拦截器只登记在<b>本模块的端点</b>的精确路径上(见 {@link InhospVisitWebConfig}),
 *       ç»ä¸ç”¨ {@code /smartor/patinhosp/**} è¿™ç§å‰ç¼€ â€”— é‚£ä¸ªå‰ç¼€ä¸‹è¿˜æœ‰å¤§é‡å‡ºå…¥é™¢è®°å½• CRUD ç«¯ç‚¹
 *       ï¼ˆ{@code selectPatMedInhospList} / {@code add} / {@code edit} / {@code remove} / {@code export} â€¦ï¼‰ï¼Œ
 *       å®ƒä»¬å±žäºŽè®°å½•管理业务,模块关闭时必须继续可用;</li>
 *   <li>模块关闭时,Controller ä¸Ž Service ä¾æ—§æ˜¯æ™®é€š Bean(启动不受影响),
 *       å…¶å®ƒæ¨¡å—通过 {@code InhospVisitFacade} è°ƒç”¨æ—¶æ‹¿åˆ°çš„æ˜¯å®‰å…¨ç©ºå€¼ï¼Œ
 *       åªæœ‰"专门调随访触发端点"的请求才会收到错误。</li>
 * </ul>
 */
@Slf4j
@Component
public class InhospVisitAvailabilityInterceptor implements HandlerInterceptor {
    /**
     * æ¨¡å—被关闭时返回给调用方的错误提示(前端可直接展示)
     * <p>
     * âš ï¸ <b>2025 æ›´æ­£</b>:本常量原先在末尾补了一句
     * ã€Œå®šæ—¶ä»»åŠ¡ä¸å—æ­¤å¼€å…³æŽ§åˆ¶ï¼Œå¦‚éœ€åœæ­¢è¯·åœ¨ã€Œç³»ç»Ÿç®¡ç† â†’ å®šæ—¶ä»»åŠ¡ã€æš‚åœå¯¹åº”çš„ä»»åŠ¡ã€â€”â€”
     * é‚£å¥è¯<b>已不成立</b>:定时任务的 5 ä¸ªå…¥å£çŽ°å·²å…¨éƒ¨æŽ¥å¼€å…³
     * ï¼ˆ4 ä¸ªèµ°é—¨é¢ã€{@code dealSubtaskDeadline} èµ°å…¥å£å®ˆå«ï¼‰ã€‚
     * æç¤ºè¯­æ˜¯è¿ç»´ç›´æŽ¥çœ‹åˆ°çš„æ–‡æœ¬ï¼Œç•™ç€å®ƒæ¯”不写更糟(会让人去停 sys_job)。
     */
    public static final String DISABLED_MESSAGE =
            "出入院随访功能未启用(参数配置 inhospvisit.enabled=false),该接口不可用";
    private final InhospVisitSwitch inhospVisitSwitch;
    public InhospVisitAvailabilityInterceptor(InhospVisitSwitch inhospVisitSwitch) {
        this.inhospVisitSwitch = inhospVisitSwitch;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        if (!inhospVisitSwitch.isEnabled()) {
            // request å…è®¸ä¸º null(单元测试直接调 preHandle æ—¶ä¸æž„造请求对象)
            String method = request == null ? "-" : request.getMethod();
            String uri = request == null ? "-" : request.getRequestURI();
            log.warn("【出入院随访】模块已关闭(参数配置 inhospvisit.enabled=false),拒绝访问 {} {}", method, uri);
            throw new ServiceException(DISABLED_MESSAGE);
        }
        return true;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package com.ruoyi.inhospvisit.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—çš„ yml é…ç½®ï¼ˆ<b>仅作兜底,不是主开关</b>)。
 *
 * <h3>⚠️ ä¸»å¼€å…³ä¸åœ¨è¿™é‡Œ</h3>
 * ä¸»å¼€å…³æ˜¯ {@code sys_config} å‚数配置表里的 {@code inhospvisit.enabled},
 * ç”± {@link InhospVisitSwitch} <b>运行时热读</b>(进程内 10 ç§’缓存,改完最多 10 ç§’生效)。
 * æœ¬ç±»åªåœ¨ã€Œsys_config é‡Œæ²¡æœ‰é‚£ä¸€è¡Œã€æˆ–「非 Spring çŽ¯å¢ƒå–ä¸åˆ°é…ç½®æœåŠ¡ã€æ—¶å…œåº•ã€‚
 *
 * <h3>为什么不放在 application.yml åšä¸»å¼€å…³</h3>
 * {@code @ConfigurationProperties} åªåœ¨å¯åŠ¨æ—¶ç»‘å®šä¸€æ¬¡ï¼Œæ”¹ yml å¿…须重启;
 * è€Œå‡ºå…¥é™¢éšè®¿æ˜¯éœ€è¦"出问题能立刻关掉"的业务。
 * ï¼ˆè¿™æ˜¯æ»¡æ„åº¦æ¨¡å—<b>实际踩过的坑</b>:最初开关放 yml,后来被迫改成 sys_config。)
 */
@Data
@Component
@ConfigurationProperties(prefix = "inhospvisit")
public class InhospVisitProperties {
    /**
     * yml å…œåº•开关(默认 true)。
     * <p>
     * å‡ºå…¥é™¢éšè®¿æ˜¯ä¸»åŠ›ä¸šåŠ¡ï¼Œå…³æŽ‰ = å½“天全院随访停摆,所以默认必须是 true;
     * è¦å…³é—­è¯·åˆ°ã€Œç³»ç»Ÿç®¡ç† â†’ å‚数设置」显式把 {@code inhospvisit.enabled} è®¾ä¸º false。
     */
    private boolean enabled = true;
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitSwitch.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,219 @@
package com.ruoyi.inhospvisit.config;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.inhospvisit.InhospVisitModule;
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>
 * å‚数键名:inhospvisit.enabled
 * å‚数键值:true  = å¯ç”¨ï¼ˆé»˜è®¤ï¼‰
 *          false = å…³é—­
 * </pre>
 * å»ºå‚数的幂等 SQL è§ {@code sql/出入院随访开关_sys_config.sql}。
 *
 * <h3>⚠️ æœ¬å¼€å…³çš„作用范围(2025 æ›´æ­£ï¼‰</h3>
 * <ul>
 *   <li><b>HTTP è§¦å‘端点</b>:由 {@code InhospVisitAvailabilityInterceptor} æ‹¦ä¸‹ï¼ŒæŠ¥æ˜Žç¡®ä¸šåŠ¡é”™è¯¯ï¼›</li>
 *   <li><b>定时任务</b>:<b>受本开关控制</b>。{@code RyTask} çš„ 5 ä¸ªå…¥å£å·²æŽ¥å¼€å…³ â€”—
 *       {@code dealOutHospInfo} / {@code dealOutHospInfoBySchedule} /
 *       {@code dealTaskGroupOutHosp} / {@code rollTaskGroupCycle} èµ°é—¨é¢ï¼Œ
 *       {@code dealSubtaskDeadline} èµ°å…¥å£å®ˆå«ï¼›å…³é—­æ—¶æ‰“
 *       ã€Œã€å‡ºå…¥é™¢éšè®¿ã€‘模块未启用(参数配置 inhospvisit.enabled=false),… è·³è¿‡ã€ï¼›</li>
 *   <li><b>基础出入院信息读写</b>({@code InhospVisitDataService}):<b>刻意不受本开关管</b> â€”—
 *       å…³æŽ‰éšè®¿ä¸è¯¥è®© HIS é‡‡é›†ä¸Žå‡ºå…¥é™¢è®°å½•管理瘫痪。</li>
 * </ul>
 * <p>
 * âš ï¸ <b>本注释此前写的是"本开关不作用于定时任务"</b>,理由是"一次误操作就能让当天全院随访停摆,
 * è€Œå®šæ—¶ä»»åŠ¡æ²¡æœ‰åé¦ˆæ¸ é“"。该说法<b>与事实不符且已更正</b>:
 * å½“时定时任务走 smartor çš„遗留实现 {@code PatMedInhospServiceImpl},
 * å®ƒ<b>从不调用门面的编排方法</b>(只用门面取数据,而数据方法按设计不受开关管),
 * æ‰€ä»¥çœŸå®žçŠ¶æ€æ˜¯"开关对定时任务无效、且无任何提示"——
 * è¿™æ¯”"受控"更糟:它让运维以为已经关掉了,实际还在生成随访。
 * çŽ°åœ¨å®šæ—¶ä»»åŠ¡èµ°é—¨é¢ï¼Œå…³é—­æœ‰æ˜Žç¡®æ—¥å¿—ï¼Œå‚æ•°åœ¨ã€Œç³»ç»Ÿç®¡ç† â†’ å‚数设置」里可见可改。
 * è¦æš‚停定时任务仍可去「系统管理 â†’ å®šæ—¶ä»»åŠ¡ã€æš‚åœ {@code sys_job},两条路都有效。
 * è¯¦è§ {@code doc/模块开关有效性核查与修复.md}。
 *
 * <h3>取值优先级</h3>
 * <ol>
 *   <li>{@code sys_config} é‡Œ {@code inhospvisit.enabled}(<b>主用</b>);</li>
 *   <li>{@link InhospVisitProperties#isEnabled()}(<b>兜底</b>,默认 true)。</li>
 * </ol>
 *
 * <h3>为什么读不到配置也按 true(fail-open)</h3>
 * è¯» {@code sys_config} è¦èµ°æ•°æ®åº“,数据库抖动时 fail-closed ä¼šæŠŠå‡ºå…¥é™¢éšè®¿æ•´ä½“打死 â€”—
 * é‚£æ˜¯æŠŠ"参数读取"变成"业务停摆放大器"。所以读失败只打 WARN å¹¶æ”¾è¡Œã€‚
 * <p>
 * âš ï¸ æ³¨æ„æœ¬æ¨¡å—另有一个"类似但不该合并"的口径:yml é‡Œçš„ {@code noLongTask}
 * ï¼ˆ{@code ServiceExternalServiceImpl.java:83-84})按 orgid ç›´æŽ¥è·³è¿‡é•¿æœŸä»»åŠ¡ç”Ÿæˆã€‚
 * è¯­ä¹‰ç›¸è¿‘但链路不同(它在 {@code ServiceExternalServiceImpl},不在随访扫描链上),
 * <b>本次不合并</b>,避免行为变更。
 *
 * <h3>为什么有 10 ç§’缓存</h3>
 * {@code SysConfigServiceImpl#selectConfigByKey} å½“前<b>每次都查库</b>
 * ï¼ˆRedis ç¼“存那段被注释掉了),而本开关每次随访请求都会被读到。
 * è¿›ç¨‹å†… TTL ç¼“存把查库降到"每实例每 10 ç§’最多一次",代价是改动最多 10 ç§’生效。
 */
@Slf4j
@Component
public class InhospVisitSwitch {
    /**
     * sys_config é‡Œçš„参数键名(与 {@link InhospVisitModule#SWITCH_KEY} ä¿æŒä¸€è‡´ï¼‰
     */
    public static final String CONFIG_KEY = InhospVisitModule.SWITCH_KEY;
    /**
     * è¿›ç¨‹å†…缓存有效期(毫秒)
     */
    private static final long CACHE_TTL_MILLIS = 10_000L;
    private final InhospVisitProperties properties;
    /**
     * å‚数配置服务(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}。
     * â€”— è¿™æ˜¯æ»¡æ„åº¦æ¨¡å—实际踩到的坑,此处刻意保留同样的写法与注释。
     */
    @Autowired
    public InhospVisitSwitch(InhospVisitProperties properties,
                             ObjectProvider<IConfigService> configServiceProvider) {
        this.properties = properties;
        this.configServiceProvider = configServiceProvider;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造:{@code configValue} å°±æ˜¯ sys_config é‡Œçš„键值,
     * ä¼  {@code null} è¡¨ç¤º"参数行不存在"(此时回落到 {@link InhospVisitProperties})。
     */
    public InhospVisitSwitch(InhospVisitProperties properties, final String configValue) {
        this(properties, 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 = properties.isEnabled();
        log.debug("【出入院随访】sys_config ä¸­æœªé…ç½® {},回落 inhospvisit.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-inhospvisit/src/main/java/com/ruoyi/inhospvisit/config/InhospVisitWebConfig.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,111 @@
package com.ruoyi.inhospvisit.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.Arrays;
import java.util.List;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—çš„ MVC é…ç½®ï¼šæŠŠ {@link InhospVisitAvailabilityInterceptor} æŒ‚到
 * <b>本模块 URL å‰ç¼€ä¸‹çš„全部端点</b>上。
 *
 * <h3>⚠️ ç”¨å‰ç¼€é€šé…ï¼Œä¸é€ä¸ªæžšä¸¾è·¯å¾„(2025 äºŒæ¬¡è°ƒæ•´ï¼‰</h3>
 * æœ¬ç±»æ›¾é€ä¸ªæžšä¸¾ 15 æ¡è·¯å¾„,理由是"可被静态断言"。但那个设计有个实际代价:
 * <b>以后往本模块加接口时,必须记得回来改这个清单</b> â€”— å¿˜äº†å°±é™é»˜ä¸å—开关管,
 * è€Œ"忘了"这件事本身不会有任何报错。
 * <p>
 * çŽ°åœ¨æ”¹ä¸ºé€šé… {@link #GUARDED_PREFIX},语义是:
 * <b>本模块 URL å‰ç¼€ä¸‹çš„æ‰€æœ‰ç«¯ç‚¹éƒ½å—开关管,新加接口自动生效,无需改配置</b>。
 * <p>
 * <b>丢掉的静态可断言性用探针补回来了,而且补得更强</b>:
 * {@code sql/_probe/verify_inhospvisit_switch.py} ä¸å†æ¯”对"清单字面量",
 * è€Œæ˜¯åšä¸¤ä»¶æ›´æœ¬è´¨çš„事:
 * <ol>
 *   <li><b>从控制器源码实测</b>端点全集,断言它们**全部**被 {@link #GUARDED_PREFIX} è¦†ç›–
 *       â€”— è¦†ç›–不到的端点会 FAIL(等价于"新加接口忘了配置"的防线);</li>
 *   <li>反射调用 {@link #addInterceptors},读 Spring å®žé™…注册的模式
 *       ï¼ˆ{@code InterceptorRegistration.getPatterns()}),断言就是 {@link #GUARDED_PREFIX}
 *       â€”— é˜²"代码写着通配、实际注册的是别的"。</li>
 * </ol>
 *
 * <h3>口径:关就整个模块的 HTTP é¢ä¸å¯ç”¨</h3>
 * ç”¨æˆ·å®žæœºæŠŠ {@code inhospvisit.enabled} è®¾ä¸º false åŽï¼Œ
 * {@code POST /smartor/patinhosp/selectPatMedInhospList} ä»èƒ½æŸ¥åˆ°æ•°æ®ã€‚
 * åŽŸè®¾è®¡æ˜¯"只拦会生成随访的触发端点",用户决定改为"关就整个模块不可用",
 * äºŽæ˜¯æœ¬å‰ç¼€ä¸‹ 15 ä¸ªç«¯ç‚¹å…¨éƒ¨å—管。
 * <p>
 * <b>但只拦 HTTP,<u>不</u>动跨模块数据契约</b>:
 * <ul>
 *   <li>✅ HTTP(本前缀下全部端点)→ å…³é—­æ—¶æŠ¥
 *       ã€Œå‡ºå…¥é™¢éšè®¿åŠŸèƒ½æœªå¯ç”¨ï¼ˆå‚æ•°é…ç½® inhospvisit.enabled=false),该接口不可用」;</li>
 *   <li>❌ {@code InhospVisitDataService}(HIS é‡‡é›†ã€æ¡£æ¡ˆæ±‡èšã€ç»Ÿè®¡ç­‰<b>模块内部/跨模块</b>访问)
 *       â†’ <b>照常可用</b>。关掉随访不该让 HIS å†™ä¸è¿›å‡ºå…¥é™¢è®°å½•
 *       ï¼ˆæ•°æ®æ–­æµæ¯”"页面看不到"严重得多,且难恢复);
 *   <li>所以"整个模块不可用"的准确含义是<b>「该模块的 HTTP é¢ä¸å¯ç”¨ã€</b>。</li>
 * </ul>
 *
 * <h3>前缀的覆盖范围(实测,非照抄注释)</h3>
 * è¯¥å‰ç¼€ä¸‹å®žé™…有 <b>15 ä¸ªç«¯ç‚¹</b>,来自三个控制器:
 * {@code InhospVisitController}(3 ä¸ªéšè®¿è§¦å‘)、
 * {@code InhospVisitDataController}(9 ä¸ªåŸºç¡€æŸ¥è¯¢/CRUD)、
 * {@code com.ruoyi.web.controller.smartor.PatMedInhospController}(3 ä¸ªå¤åˆç«¯ç‚¹ï¼Œ
 * å®žçŽ°ä»ç•™åœ¨ smartor ä½† URL å±žæœ¬å‰ç¼€ï¼‰ã€‚
 * <p>
 * âš ï¸ æ—§æ³¨é‡Šå†™"14 ä¸ªç«¯ç‚¹ / å¤åˆç«¯ç‚¹ 4 ä¸ª",实测是 15 / 3 â€”— è¿™ç±»"注释里的数字"
 * æ­£æ˜¯å£å¾„混乱的来源,所以探针改为从源码实测。
 */
@Configuration
public class InhospVisitWebConfig implements WebMvcConfigurer {
    /**
     * æœ¬æ¨¡å—çš„ URL å‰ç¼€ã€‚模块内所有控制器都挂在它下面
     * ï¼ˆ{@code InhospVisitController} ä¸Ž {@code InhospVisitDataController} å®žæµ‹å‡ä¸º
     * {@code @RequestMapping("/smartor/patinhosp")})。
     * <p>
     * âš ï¸ åŠ æ–°æŽ§åˆ¶å™¨æ—¶<b>必须</b>也用这个前缀 â€”— å¦åˆ™å®ƒä¸ä¼šå—开关管。
     * æŽ¢é’ˆä¼šä»Žæºç å®žæµ‹æ‰€æœ‰æŽ§åˆ¶å™¨çš„ {@code @RequestMapping} ä¸Žç«¯ç‚¹ï¼Œ
     * å‡¡è¦†ç›–不到本前缀的都会 FAIL。
     */
    public static final String MODULE_URL_PREFIX = "/smartor/patinhosp";
    /**
     * æ‹¦æˆªå™¨å®žé™…注册的模式(通配整个前缀)。
     */
    public static final String GUARDED_PREFIX = MODULE_URL_PREFIX + "/**";
    @Autowired
    private InhospVisitAvailabilityInterceptor inhospVisitAvailabilityInterceptor;
    /**
     * ä¾›æµ‹è¯•/非 Spring çŽ¯å¢ƒç›´æŽ¥æž„é€ 
     */
    public InhospVisitWebConfig() {
    }
    public InhospVisitWebConfig(InhospVisitAvailabilityInterceptor interceptor) {
        this.inhospVisitAvailabilityInterceptor = interceptor;
    }
    /**
     * æœ¬æ¨¡å—需要"关闭即明确报错"的路径模式。
     * <p>
     * âš ï¸ åªæœ‰ä¸€æ¡é€šé…ï¼ˆè§ç±»æ³¨é‡Šçš„取舍说明)。
     * <b>不要</b>把它当成"受管端点清单"用 â€”—
     * çœŸå®žç«¯ç‚¹æ¸…单请用 {@code sql/_probe/verify_inhospvisit_switch.py} ä»ŽæŽ§åˆ¶å™¨æºç å®žæµ‹ã€‚
     */
    public static List<String> guardedPathPatterns() {
        return new ArrayList<>(Arrays.asList(GUARDED_PREFIX));
    }
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(inhospVisitAvailabilityInterceptor)
                .addPathPatterns(guardedPathPatterns())
                // æ‹¦æˆªå™¨æœ¬èº«å¾ˆè½»ï¼ˆåªè¯»ä¸€ä¸ª boolean),order ç»™ä¸ªå°å€¼è®©å®ƒåœ¨ä¸šåŠ¡æ‹¦æˆªå™¨ä¹‹å‰æ‰§è¡Œ
                .order(-100);
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/controller/InhospVisitController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
package com.ruoyi.inhospvisit.controller;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.inhospvisit.api.InhospVisitFacade;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * å‡ºå…¥é™¢éšè®¿<b>触发端点</b> â€”— ä»Ž {@code ruoyi-admin} çš„ {@code PatMedInhospController} è¿å…¥ã€‚
 *
 * <h3>⚠️ URL ä¸€ä¸ªéƒ½ä¸èƒ½å˜</h3>
 * ç±»çº§è·¯å¾„仍是 {@code /smartor/patinhosp},三个方法级路径逐字照抄:
 * <ul>
 *   <li>{@code POST /smartor/patinhosp/dealOutHospInfo}</li>
 *   <li>{@code POST /smartor/patinhosp/dealOutHospInfoBySchedule}</li>
 *   <li>{@code POST /smartor/patinhosp/addOperationSubTask}</li>
 * </ul>
 * çœŸå®žå‰ç«¯åœ¨ä»“库外({@code D:\daima\smartor\smartor-web},代理 {@code localhost:8095}),
 * æ”¹äº†åœ°å€å°±æ— æ³•回归。由 {@code sql/_probe/verify_inhospvisit_url_equivalence.py} æŠŠå…³ã€‚
 *
 * <h3>迁入时同步删除</h3>
 * {@code PatMedInhospController} é‡Œå¯¹åº”è¿™ 3 ä¸ªæ–¹æ³•çš„
 * {@code @PostMapping} ä¸Ž {@code @ApiOperation} å¿…é¡»<b>同时删掉</b> â€”—
 * å¦åˆ™ä¸¤ä¸ªç±»éƒ½æ˜ å°„同一路径,Spring ä¼šå›  {@code Ambiguous mapping} <b>直接启动失败</b>。
 *
 * <h3>与原 Controller çš„两处差异(等价改写,已登记)</h3>
 * <ol>
 *   <li><b>不再注入 {@code ISysConfigService}</b>:{@code visit.early.day} æ”¹ç”±
 *       {@link InhospVisitTaskService} å†…部读(它本来就要用这个值)。
 *       åŽŸå®žçŽ°æ˜¯åœ¨ Controller é‡Œè¯»å¥½å†ä¼ è¿›åŽ»çš„ï¼Œæ•ˆæžœä¸€è‡´ã€‚</li>
 *   <li><b>{@code active} åªç”¨äºŽ"是否做门诊随访"的站点判断</b>(原 L263 çš„
 *       {@code if (!active.equals("xh"))}),与原实现同一位置、同一写法。</li>
 * </ol>
 *
 * <h3>开关如何生效</h3>
 * æœ¬æ¨¡å—çš„ {@code InhospVisitAvailabilityInterceptor} é€šè¿‡
 * {@code InhospVisitWebConfig} æŒ‚在这两条精确路径上:
 * <pre>
 * /smartor/patinhosp/dealOutHospInfo
 * /smartor/patinhosp/dealOutHospInfoBySchedule
 * </pre>
 * è¿å…¥åŽ"模块自己的端点 + æ¨¡å—自己的拦截器"终于同处一个模块,
 * ä¸å†æ˜¯è·¨æ¨¡å—注册(那是本次改造期间的一个临时状态)。
 * <p>
 * âš ï¸ {@code /addOperationSubTask} <b>刻意不挂拦截器</b>:它的实现本次未搬
 * ï¼ˆå±žç¬¬äºŒæ‰¹ {@code ruoyi-othervisit}),挂上会让"还没搬走的端点"也受本开关影响。
 */
@Slf4j
@Api(description = "出入院随访触发")
@RestController
@RequestMapping("/smartor/patinhosp")
public class InhospVisitController {
    /** ç«™ç‚¹ï¼šæ–°åŽï¼ˆæš‚时不做门诊随访) */
    private static final String SITE_XH = "xh";
    /** å‡ºé™¢éšè®¿ã€Œé€šçŸ¥ç±»ä»»åŠ¡ã€æå‰å‡ å¤©å‘ï¼ˆåŽŸ Controller ä»Ž sys_config è¯»çš„就是这个键) */
    private static final String VISIT_EARLY_DAY_KEY = "visit.early.day";
    @Value("${spring.profiles.active}")
    private String active;
    @Autowired
    private InhospVisitFacade inhospVisitFacade;
    /**
     * å‚数配置服务(ruoyi-common çš„æŽ¥å£ï¼Œå®žçŽ°åœ¨ ruoyi-system)。
     * <p>
     * âš ï¸ ç”¨ {@code ObjectProvider} æƒ°æ€§å–:本模块只依赖 ruoyi-common,
     * åœ¨ç‹¬ç«‹éƒ¨ç½²/测试等"容器里没有实现"的场景下不能因此启动失败。
     * <p>
     * âš ï¸ <b>必须由 Controller è¯»ã€ä¸èƒ½ä¼ ç»™ Service åŽ»è¯»</b>:
     * åŽŸå®žçŽ°å°±æ˜¯"Controller è¯»å¥½å†ä¼ è¿›åŽ»"(原 L259/L274/L285),
     * Service å±‚收的是<b>值</b>。保持这个形状,行为才一致。
     */
    @Autowired
    private ObjectProvider<IConfigService> configServiceProvider;
    /**
     * å‡ºé™¢éšè®¿ä¸»æ‰«æ + é—¨è¯Šéšè®¿ï¼ˆåŽŸ {@code PatMedInhospController#dealOutHospInfo} L256-266)。
     * <p>
     * é€è¡Œå¯¹åº”:
     * <pre>
     * String config = configService.selectConfigByKey("visit.early.day");
     * //出院表
     * iPatMedInhospService.dealOutHospInfo(config);
     * //门诊表(新华的暂时不做门诊随访)
     * if (!active.equals("xh")) {
     *     iPatMedOuthospService.dealOutpatientInfo();
     * }
     * </pre>
     * æ¬è¿åŽ {@code config} ç”± {@link InhospVisitTaskService} å†…部读取。
     * è¿”回类型保持 {@code void}(与前端契约一致),<b>不改出参</b>。
     */
    @ApiOperation("dealOutHospInfo")
    @PostMapping("/dealOutHospInfo")
    public void dealOutHospInfo() {
        // åŽŸ L259:String config = configService.selectConfigByKey("visit.early.day");
        String config = readVisitEarlyDay();
        //出院表(含入院宣教 / è¯å“å®£æ•™ / è§„则编排 / æ‰‹æœ¯éšè®¿ / å†å…¥é™¢å¤„理)
        inhospVisitFacade.dealOutHospInfo(config);
        //门诊表(新华的暂时不做门诊随访)
        if (!SITE_XH.equals(active)) {
            inhospVisitFacade.dealOutpatientInfo();
        }
    }
    /**
     * ä»»åŠ¡ç»„é¦–è½®çº³å…¥ï¼ˆåŽŸ {@code PatMedInhospController#dealOutHospInfoBySchedule} L271-277)。
     * <p>
     * âš ï¸ å®žçŽ°æœ¬æ¬¡<b>未搬迁</b>(含手术分支与任务组 builder,属第二批范围),
     * ç”±é—¨é¢è½¬å‘到 smartor çš„实现 â€”— è¡Œä¸ºä¸ŽåŽŸ Controller ç›´æŽ¥è°ƒç”¨å®Œå…¨ä¸€è‡´ã€‚
     */
    @ApiOperation("dealOutHospInfoBySchedule")
    @PostMapping("/dealOutHospInfoBySchedule")
    public void dealOutHospInfoBySchedule() {
        // åŽŸ L274:String config = configService.selectConfigByKey("visit.early.day");
        inhospVisitFacade.dealOutHospInfoBySchedule(readVisitEarlyDay());
    }
    /**
     * æ‰‹æœ¯éšè®¿æ‰‹åŠ¨è§¦å‘ï¼ˆåŽŸ {@code PatMedInhospController#addOperationSubTask} L282-287)。
     * <p>
     * âš ï¸ å®žçŽ°æœ¬æ¬¡<b>未搬迁</b>(属第二批 {@code ruoyi-othervisit}),
     * ç”±é—¨é¢è½¬å‘到 smartor çš„实现。
     */
    @ApiOperation("addOperationSubTask")
    @PostMapping("/addOperationSubTask")
    public void addOperationSubTask() {
        // åŽŸ L285:String config = configService.selectConfigByKey("visit.early.day");
        inhospVisitFacade.addOperationSubTask(readVisitEarlyDay());
    }
    /**
     * è¯» {@code sys_config: visit.early.day}。
     * <p>
     * å¯¹åº”原 Controller ä¸‰å¤„ {@code configService.selectConfigByKey("visit.early.day")}。
     * å–不到(没有实现 / è¯»åº“异常)时返回 {@code null} å¹¶æ‰“ WARN â€”—
     * åŽŸå®žçŽ°åœ¨è¿™ç§æƒ…å†µä¸‹æ‹¿åˆ°çš„ä¹Ÿæ˜¯ {@code null}({@code selectConfigByKey} æŸ¥ä¸åˆ°å³è¿”回 null),
     * æ‰€ä»¥è¯­ä¹‰ä¸€è‡´ã€‚
     */
    private String readVisitEarlyDay() {
        if (configServiceProvider == null) {
            return null;
        }
        try {
            IConfigService svc = configServiceProvider.stream().findFirst().orElse(null);
            if (svc == null) {
                log.warn("【出入院随访】容器里没有 IConfigService å®žçŽ°ï¼Œ{} æŒ‰æœªé…ç½®ï¼ˆnull)处理。",
                        VISIT_EARLY_DAY_KEY);
                return null;
            }
            return svc.selectConfigByKey(VISIT_EARLY_DAY_KEY);
        } catch (Exception e) {
            log.warn("【出入院随访】读取 {} å¤±è´¥ï¼ŒæŒ‰æœªé…ç½®ï¼ˆnull)处理:{}",
                    VISIT_EARLY_DAY_KEY, e.toString());
            return null;
        }
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/controller/InhospVisitDataController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,240 @@
package com.ruoyi.inhospvisit.controller;
import com.github.pagehelper.ISelect;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.annotation.AddOrgId;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.inhospvisit.api.InhospVisitFacade;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.inhospvisit.domain.PatMedInhospDTO;
import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
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.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
 * åŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯<b>查询 / CRUD ç«¯ç‚¹</b> â€”— ä»Ž {@code ruoyi-admin} çš„
 * {@code PatMedInhospController} è¿å…¥ï¼ˆ"其它模块只能从本模块调用出入院信息"的落地点之一)。
 *
 * <h3>⚠️ URL ä¸€ä¸ªéƒ½ä¸èƒ½å˜</h3>
 * ç±»çº§è·¯å¾„ {@code /smartor/patinhosp} ä¸ŽåŽŸ Controller é€å­—一致(本类与
 * {@link InhospVisitController} å…±ç”¨è¯¥å‰ç¼€ï¼Œä½†<b>方法级路径完全不重叠</b>,
 * å› æ­¤ä¸ä¼š {@code Ambiguous mapping});11 ä¸ªæ–¹æ³•级路径也逐字照抄。
 * çœŸå®žå‰ç«¯åœ¨ä»“库外({@code D:\daima\smartor\smartor-web}),改了地址就无法回归。
 * ç”± {@code sql/_probe/verify_inhospvisit_url_equivalence.py} æŠŠå…³ã€‚
 *
 * <h3>数据访问路径</h3>
 * æœ¬ç±»<b>不注入任何 smartor çš„ Service</b>,一律走本模块门面
 * {@link InhospVisitFacade}(它实现了 {@code InhospVisitDataService})。
 * è¿™ä¹Ÿæ­£æ˜¯æœ¬æ¬¡æ”¹é€ è¦è¾¾æˆçš„事实:出入院信息只有本模块一处实现。
 *
 * <h3>哪 4 ä¸ªç«¯ç‚¹<b>没有</b>迁进来(及其原因)</h3>
 * <ul>
 *   <li>{@code /getDeptRanking} â€”— SQL åœ¨ {@code ServiceSubtaskMapper.getDeptRanking},
 *       å±žå­ä»»åŠ¡åŸŸè€Œéžå‡ºå…¥é™¢ä¿¡æ¯ï¼›</li>
 *   <li>{@code /selectPatMedInhospByApplication} â€”— ä¾èµ– {@code ServiceSubtaskMapper}
 *       ä¸Ž {@code PatMedInhospDTO} è½¬æ¢ï¼›<b>且原实现有反向逻辑缺陷</b>(见 10.4.10 æ³¨é‡Šï¼‰ï¼›</li>
 *   <li>{@code /getDocAndPat} â€”— è¦åˆå¹¶ {@code PatArchiveImport}(档案导入域)的数据;</li>
 *   <li>{@code /getDeptCodeByPatId} â€”— ä¾èµ– {@code PatMedInhospDTO} è½¬æ¢é“¾ã€‚</li>
 * </ul>
 * è¿™ 4 ä¸ªä»ç”± {@code com.ruoyi.web.controller.smartor.PatMedInhospController} æ‰¿è½½ï¼ŒURL ä¸å˜ã€‚
 *
 * <h3>开关如何生效</h3>
 * æœ¬æ¨¡å—çš„ {@code InhospVisitAvailabilityInterceptor} åªæŒ‚在<b>随访触发的精确路径</b>上
 * ï¼ˆ{@code /dealOutHospInfo}、{@code /dealOutHospInfoBySchedule}),
 * <b>不含本类的任何数据端点</b> â€”— ä¸Žè¿å…¥å‰çš„行为完全一致:
 * å…³é—­ {@code inhospvisit.enabled} åªåœ"随访生成",不会让出入院记录的查询/CRUD ä¸å¯ç”¨ã€‚
 * ä¾æ®æ˜¯ {@code sys_config: inhospvisit.enabled} çš„æœ¬æ„ï¼ˆå…³é—­ = å½“天不再生成随访),
 * è€Œä¸æ˜¯"关闭整个模块"。这一点在 {@code InhospVisitWebConfig} é‡Œæœ‰å¯¹åº”注释。
 */
@Slf4j
@Api(description = "基础出入院信息")
@RestController
@RequestMapping("/smartor/patinhosp")
public class InhospVisitDataController extends BaseController {
    /**
     * æœ¬æ¨¡å—常驻门面(同时实现随访契约与数据契约)。
     * <p>
     * âš ï¸ æ³¨å…¥é—¨é¢è€Œä¸æ˜¯ {@code PatMedInhospMapper}:门面永远存在,
     * ä¸ä¼šå› å¼€å…³çŠ¶æ€æˆ–è£…é…å·®å¼‚å¯¼è‡´æœ¬ Controller å¯åŠ¨å¤±è´¥ã€‚
     */
    @Autowired
    private InhospVisitFacade inhospVisitFacade;
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录列表。
     * <p>
     * åŽŸ {@code PatMedInhospController#selectPatMedInhosplist}(L77-87)逐行对应。
     */
    @ApiOperation("查询患者住院记录列表")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:list')")
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/selectPatMedInhospList")
    public TableDataInfo selectPatMedInhosplist(@RequestBody PatMedInhosp patMedInhosp) {
        PageUtils.startPageByPost(patMedInhosp.getPageNum(), patMedInhosp.getPageSize());
        SysUser user = getLoginUser().getUser();
        patMedInhosp.setOrgid(user.getOrgid());
        List<PatMedInhosp> list = inhospVisitFacade.selectPatMedInhospList(patMedInhosp);
        // startPage ä¹‹åŽ PageHelper å·²ç»è‡ªåŠ¨æ‰§è¡Œè¿‡ä¸€æ¬¡ count æŸ¥è¯¢ï¼Œç›´æŽ¥å¤ç”¨ Page é‡Œçš„ total,
        // é¿å…ç”¨ PageUtils.count æŠŠå¸¦ JOIN çš„统计语句再跑一遍
        long count = new PageInfo<>(list).getTotal();
        return getDataTable2(count, list);
    }
    /**
     * æŸ¥è¯¢å½“前登陆人自己(病区、部门)的患者住院记录列表。
     * <p>
     * åŽŸ {@code PatMedInhospController#selectPatMedInhospListByCondition}(L126-142)逐行对应。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°é‡Œä¸¤ä¸ªåˆ†æ”¯è°ƒç”¨çš„æ˜¯<b>同一个方法</b>(只是条件不同),此处原样保留 â€”—
     * åˆå¹¶åˆ†æ”¯ä¼šæ”¹å˜"两个条件都为空时 list ä¿æŒ null"这一既有行为。
     */
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @ApiOperation("查询当前登陆人自己(病区、部门)的患者住院记录列表")
    @PostMapping("/selectPatMedInhospListByCondition")
    public TableDataInfo selectPatMedInhospListByCondition(@RequestBody PatMedInhospVO patMedInhospVO) {
        PageUtils.startPageByPost(patMedInhospVO.getPageNum(), patMedInhospVO.getPageSize());
        List<PatMedInhosp> list = null;
        if (!CollectionUtils.isEmpty(patMedInhospVO.getLeavehospitaldistrictcodes())) {
            list = inhospVisitFacade.selectPatMedInhospListByCondition(patMedInhospVO);
        } else if (!CollectionUtils.isEmpty(patMedInhospVO.getLeaveldeptcodes())) {
            list = inhospVisitFacade.selectPatMedInhospListByCondition(patMedInhospVO);
        }
        long count = PageUtils.count(new ISelect() {
            @Override
            public void doSelect() {
                inhospVisitFacade.selectPatMedInhospListByCondition(patMedInhospVO);
            }
        });
        return getDataTable2(count, list);
    }
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录通过入院申请单信息。
     * <p>
     * åŽŸ {@code PatMedInhospController#selectPatMedInhospByApplication} é€è¡Œå¯¹åº”
     * ï¼ˆå«åˆ†é¡µã€{@code @AddOrgId}、以及 count ç”¨çš„ {@code ISelect})。
     * <p>
     * âš ï¸ <b>本端点原有的反向判断缺陷被原样保留</b>(见 {@code InhospVisitDataService}
     * å¯¹è¯¥æ–¹æ³•的说明):查到记录时服务层返回 {@code null},所以本端点实际恒返回空数据。
     * å‰ç«¯å½“前没有调用它(已核对 {@code smartor-web/src})。
     * æŒ‰"原实现怪癖一律原样保留、只在注释标注"的纪律,本步<b>不修</b>它。
     */
    @ApiOperation("查询患者住院记录通过入院申请单信息")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:list')")
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/selectPatMedInhospByApplication")
    public TableDataInfo selectPatMedInhospByApplication(@RequestBody PatMedInhosp patMedInhosp) {
        PageUtils.startPageByPost(patMedInhosp.getPageNum(), patMedInhosp.getPageSize());
        SysUser user = getLoginUser().getUser();
        patMedInhosp.setOrgid(user.getOrgid());
        List<PatMedInhospDTO> list = inhospVisitFacade.selectPatMedInhospByApplication(patMedInhosp);
        long count = PageUtils.count(new ISelect() {
            @Override
            public void doSelect() {
                inhospVisitFacade.selectPatMedInhospByApplication(patMedInhosp);
            }
        });
        return getDataTable2(count, list);
    }
    /**
     * æŸ¥è¯¢å‡ºã€å…¥é™¢çœ‹ç—…人次和人数(人数分为首次服务、再次服务、专病服务)。
     * <p>
     * åŽŸ {@code PatMedInhospController#selectPatMedInhospListCount}(L153-160)逐行对应。
     * å‰ç«¯é¦–页 ECharts åœ¨ç”¨ï¼ˆ{@code src/api/AiCentre/EChartsdata.js}),地址不可变。
     */
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/selectPatMedInhospListCount")
    @ApiOperation("查询出、入院看病人次和人数")
    public AjaxResult selectPatMedInhospListCount(@RequestBody PatMedReq patMedReq) {
        SysUser user = getLoginUser().getUser();
        patMedReq.setOrgid(user.getOrgid());
        return success(inhospVisitFacade.selectPatMedInhospCount(patMedReq));
    }
    /**
     * å¯¼å‡ºæ‚£è€…住院记录列表。
     * <p>
     * åŽŸ {@code PatMedInhospController#export}(L165-173)逐行对应。
     */
    @ApiOperation("导出患者住院记录列表")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:export')")
    @Log(title = "患者住院记录", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, PatMedInhosp patMedInhosp) {
        List<PatMedInhosp> list = inhospVisitFacade.selectPatMedInhospList(patMedInhosp);
        ExcelUtil<PatMedInhosp> util = new ExcelUtil<PatMedInhosp>(PatMedInhosp.class);
        util.exportExcel(response, list, "患者住院记录数据");
    }
    /**
     * èŽ·å–æ‚£è€…ä½é™¢è®°å½•è¯¦ç»†ä¿¡æ¯ã€‚
     * <p>
     * åŽŸ {@code PatMedInhospController#getInfo}(L178-183)逐行对应。
     */
    @ApiOperation("获取患者住院记录详细信息")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:query')")
    @GetMapping(value = "/getInfo/{inhospid}")
    public AjaxResult getInfo(@PathVariable("inhospid") Long inhospid) {
        return success(inhospVisitFacade.selectPatMedInhospByInhospid(inhospid));
    }
    /**
     * æ–°å¢žæ‚£è€…住院记录。
     * <p>
     * åŽŸ {@code PatMedInhospController#add}(L188-197)逐行对应。
     */
    @ApiOperation("新增患者住院记录")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:add')")
    @Log(title = "患者住院记录", businessType = BusinessType.INSERT)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/add")
    public AjaxResult add(@RequestBody PatMedInhosp patMedInhosp) {
        SysUser user = getLoginUser().getUser();
        patMedInhosp.setOrgid(user.getOrgid());
        return toAjax(inhospVisitFacade.insertPatMedInhosp(patMedInhosp));
    }
    /**
     * ä¿®æ”¹æ‚£è€…住院记录。
     * <p>
     * åŽŸ {@code PatMedInhospController#edit}(L202-209)逐行对应。
     */
    @ApiOperation("修改患者住院记录")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:edit')")
    @Log(title = "患者住院记录", businessType = BusinessType.UPDATE)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/edit")
    public AjaxResult edit(@RequestBody PatMedInhosp patMedInhosp) {
        return toAjax(inhospVisitFacade.updatePatMedInhosp(patMedInhosp));
    }
    /**
     * åˆ é™¤æ‚£è€…住院记录。
     * <p>
     * åŽŸ {@code PatMedInhospController#remove}(L214-220)逐行对应。
     */
    @ApiOperation("删除患者住院记录")
    //@PreAuthorize("@ss.hasPermi('smartor:patinhosp:remove')")
    @Log(title = "患者住院记录", businessType = BusinessType.DELETE)
    @GetMapping("/remove/{inhospids}")
    public AjaxResult remove(@PathVariable Long[] inhospids) {
        return toAjax(inhospVisitFacade.deletePatMedInhospByInhospids(inhospids));
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/PatMedInhosp.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,589 @@
package com.ruoyi.inhospvisit.domain;
import java.util.Date;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;
/**
 * æ‚£è€…住院记录对象 pat_med_inhosp
 *
 * @author smartor
 * @date 2023-03-04
 */
@ApiModel(value = "PatMedInhosp", description = "患者住院记录对象")
@Data
public class PatMedInhosp extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty(value = "自增ID")
    private Long inhospid;
    /**
     * æ€§åˆ«
     */
    @ApiModelProperty(value = "性别 1男  2女")
    private Long sex;
    /**
     * ä½é™¢å·
     */
    @ApiModelProperty(value = "住院号")
    private String inhospno;
    /**
     * æµæ°´å·
     */
    @ApiModelProperty(value = "流水号")
    private String serialnum;
    /**
     * æŠ¤å£«ID
     */
    @ApiModelProperty(value = "护士ID")
    private String nurseId;
    /**
     * æŠ¤å£«å§“名
     */
    @ApiModelProperty(value = "护士姓名")
    private String nurseName;
    /**
     * åŒ»é™¢åç§°
     */
    @ApiModelProperty(value = "医院名称")
    @Excel(name = " åŒ»é™¢åç§° ")
    private String hospitalname;
    /**
     * åŒ»é™¢ç¼–号
     */
    @ApiModelProperty(value = "医院编号")
    private String hospitalcode;
    /**
     * é™¢åŒºç¼–号
     */
    @ApiModelProperty(value = "院区编号")
    private String hospitaldistrictcode;
    /**
     * ç—…区编号集合
     */
    @ApiModelProperty(value = "病区编号集合")
    private List<String> hospitaldistrictcodeList;
    /**
     * é™¢åŒºåç§°
     */
    @ApiModelProperty(value = "病区名称")
    @Excel(name = " ç—…区名称 ")
    private String hospitaldistrictname;
    /**
     * å…¥é™¢è¯Šæ–­ICD值
     */
    @ApiModelProperty(value = "入院诊断ICD值")
    private String icd10code;
    /**
     * å…¥é™¢è¯Šæ–­ICD值编号集合
     */
    @ApiModelProperty(value = "入院诊断ICD值编号集合")
    private List<String> icd10codeList;
    /**
     * å…¥é™¢è¯Šæ–­åç§°
     */
    @ApiModelProperty(value = "入院诊断名称")
    private String diagname;
    /**
     * å…¥é™¢æ—¶é—´
     */
    @ApiModelProperty(value = "入院时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = " å…¥é™¢æ—¶é—´ ", width = 30, dateFormat = "yyyy-MM-dd")
    private Date starttime;
    /**
     * å‡ºé™¢æ—¶é—´
     */
    @ApiModelProperty(value = "出院时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = " å‡ºé™¢æ—¶é—´ ", width = 30, dateFormat = "yyyy-MM-dd")
    private Date endtime;
    /**
     * å…¥é™¢ç§‘室代码
     */
    @ApiModelProperty(value = "入院科室代码")
    private String deptcode;
    /**
     * å…¥é™¢ç§‘室代码
     */
    @ApiModelProperty(value = "入院科室代码集合")
    private List<String> deptcodeList;
    /**
     * å…¥é™¢ç§‘室名称
     */
    @ApiModelProperty(value = "入院科室名称")
    private String deptname;
    /**
     * æˆ¿é—´å·
     */
    @ApiModelProperty(value = "房间号")
    private String roomno;
    /**
     * åºŠä½å·
     */
    @ApiModelProperty(value = "床位号")
    @Excel(name = " åºŠä½å· ")
    private String bedNo;
    /**
     * æœºæž„ID
     */
    @ApiModelProperty(value = "机构ID")
    @Excel(name = " æœºæž„ID ")
    private String orgid;
    /**
     * åˆ é™¤æ ‡è®°
     */
    @ApiModelProperty(value = "删除标记")
    private String delFlag;
    /**
     * ä¸Šä¼ æ ‡è®°
     */
    @ApiModelProperty(value = "上传标记")
    private Long isupload;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    @ApiModelProperty(value = "上传时间")
    private Date uploadTime;
    /**
     * æ¡£æ¡ˆID
     */
    @ApiModelProperty(value = "档案ID")
    private Long patid;
    /**
     * å‡ºé™¢è¯Šæ–­
     */
    @ApiModelProperty(value = "出院诊断")
    @Excel(name = " å‡ºé™¢è¯Šæ–­ ")
    private String leavediagname;
    /**
     * å‡ºé™¢è¯Šæ–­ICD值
     */
    @ApiModelProperty(value = "出院诊断ICD值")
    private String leaveicd10code;
    /**
     * åŒ»ç”Ÿå·¥å·
     */
    @ApiModelProperty(value = "医生工号")
    private String drcode;
    /**
     * åŒ»ç”Ÿåç§°
     */
    @ApiModelProperty(value = "医生名称")
    @Excel(name = " åŒ»ç”Ÿåç§° ")
    private String drname;
    /**
     * æ˜¯å¦ç”Ÿæˆæ–¹æ¡ˆçŠ¶æ€;0预入院 1生成入院 2生成出院 9无匹配方案   3生成入院撤销   4生成出院撤销
     */
    @ApiModelProperty(value = "是否生成方案状态;0预入院 1生成入院 2生成出院 9无匹配方案   3生成入院撤销   4生成出院撤销")
    private Long schemestatus;
    /**
     * æ˜¯å¦ç”Ÿæˆé€šç”¨æ–¹æ¡ˆçŠ¶æ€;0未生成 1生成 9无匹配方案
     */
    @ApiModelProperty(value = "是否生成通用方案状态;0未生成 1生成 9无匹配方案")
    private Long generalschemestatus;
    /**
     * å‡ºé™¢ç§‘室代码
     */
    @ApiModelProperty(value = "出院科室代码")
    private String leaveldeptcode;
    /**
     * å‡ºé™¢ç§‘室名称
     */
    @ApiModelProperty(value = "出院科室名称")
    @Excel(name = " å‡ºé™¢ç§‘室名称 ")
    private String leaveldeptname;
    /**
     * ç—…区ID
     */
    @ApiModelProperty(value = "病区ID")
    private String hospitaldistrictid;
    /**
     * å‡ºé™¢ç—…区编号
     */
    @ApiModelProperty(value = "出院病区编号")
    private String leavehospitaldistrictcode;
    /**
     * å‡ºé™¢ç—…区名称
     */
    @ApiModelProperty(value = "出院病区名称")
    @Excel(name = " å‡ºé™¢ç—…区名称 ")
    private String leavehospitaldistrictname;
    /**
     * å‡ºé™¢ç—…区ID
     */
    @ApiModelProperty(value = "出院病区ID")
    private String leavehospitaldistrictid;
    /**
     * å…¥é™¢ç§‘室ID
     */
    @ApiModelProperty(value = "入院科室ID")
    private String deptid;
    /**
     * å‡ºé™¢ç§‘室ID
     */
    @ApiModelProperty(value = "出院科室ID")
    private String leaveldeptid;
    /**
     * æ–¹æ¡ˆçŠ¶æ€æ“ä½œæ—¶é—´
     */
    @ApiModelProperty(value = "方案状态操作时间")
    private Date schemetime;
    /**
     * æ‚£è€…姓名
     */
    @ApiModelProperty(value = "患者姓名")
    private String patname;
    /**
     * æ‚£è€…编号
     */
    @ApiModelProperty(value = "患者编号")
    private String patno;
    /**
     * ä½é™¢æ ‡è¯†ï¼š 0:在院  1:出院    2:转科
     */
    @ApiModelProperty(value = "住院标识: 0:在院  1:出院    2:转科  3待入院")
    private String inhospstate;
    /**
     * ç§‘室长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String deptcheckFlag;
    /**
     * ç—…区长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "病区长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String wardcheckFlag;
    /**
     * ç–¾ç—…长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "疾病长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String diagcheckFlag;
    /**
     * ä»»åŠ¡ç»„ï¼ˆappltype=5)首轮纳入标记:0未处理  1已生成首轮  2无匹配/失败
     * ç‹¬ç«‹äºŽ dept/ward/diag check_flag,专供任务组独立扫描使用;后续轮次由 rollTaskGroupCycle è´Ÿè´£ã€‚
     */
    @ApiModelProperty(value = "任务组(appltype=5)首轮纳入标记:0未处理 1已生成首轮 2无匹配/失败")
    private String scheduleCheckFlag;
    /**
     * å…¥é™¢ç§‘室长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "入院科室长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String inDeptcheckFlag;
    /**
     * å…¥é™¢ç—…区长期任务,是否处理:0 æœªå¤„理   1 å¤„理
     */
    @ApiModelProperty(value = "入院病区长期任务,是否处理:0 æœªå¤„理   1 å¤„理成功    2 å¤„理失败 ")
    private String inWardcheckFlag;
    /**
     * è¯å“å®£æ•™é•¿æœŸä»»åŠ¡ï¼Œæ˜¯å¦å¤„ç†ï¼š0 æœªå¤„理   1 å¤„理
     */
    @ApiModelProperty(value = "药品宣教长期任务,是否处理:0 æœªå¤„理   1 å¤„理成功    2 å¤„理失败 ")
    private String medcheckFlag;
    /**
     * å¹´é¾„
     */
    @ApiModelProperty(value = "年龄")
    private String age;
    /**
     * æ‰‹æœºå·
     */
    @ApiModelProperty(value = "手机号")
    private String telcode;
    /**
     * é•¿æœŸä»»åŠ¡åŽŸå› 
     */
    @ApiModelProperty(value = "长期任务原因")
    private String longTaskReason;
    /**
     * pageNum
     */
    @ApiModelProperty("pageNum")
    private Integer pageNum;
    /**
     * pageSize
     */
    @ApiModelProperty("pageSize")
    private Integer pageSize;
    /**
     * pageNum
     */
    @ApiModelProperty("pn")
    private Integer pn;
    /**
     * pageSize
     */
    @ApiModelProperty("ps")
    private Integer ps;
    /**
     * å‡ºé™¢æ–¹å¼åºå·
     */
    @ApiModelProperty("出院方式序号")
    @Excel(name = "出院方式序号")
    private String outWayId;
    /**
     * å‡ºé™¢æ–¹å¼åç§°
     */
    @ApiModelProperty("出院方式名称")
    @Excel(name = "出院方式名称")
    private String outWayName;
    /**
     * å‡ºå…¥é™¢æ ‡è¯†
     */
    @ApiModelProperty("出入院标识:0入院  1出院 2 è½¬ç§‘  3 é¢„入院  4.满意度")
    private Integer cry;
    /**
     * å…¥é™¢åºŠä½æŽ’序:asc(或 1)=正序,desc(或 2)=倒序,不传=不排序
     * <p>
     * åªå¯¹ã€Œåœ¨é™¢ã€æ‚£è€…查询(cry=0)生效,其它查询忽略该参数。
     */
    @ApiModelProperty("入院床位排序:asc/1=正序,desc/2=倒序,不传=不排序(仅 cry=0 åœ¨é™¢æ‚£è€…生效)")
    private String bedSort;
    /**
     * å‡ºé™¢ç—…区编号
     */
    @ApiModelProperty(value = "出院病区编号集合")
    private List<String> leavehospitaldistrictcodes;
    /**
     * å‡ºé™¢ç§‘室代码集合
     */
    @ApiModelProperty(value = "出院科室代码集合")
    private List<String> leaveldeptcodes;
    /**
     * å¼€å§‹å‡ºé™¢æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "开始出院日期")
    private Date startOutHospTime;
    /**
     * ç»“束出院日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "结束出院日期")
    private Date endOutHospTime;
    /**
     * å¼€å§‹å…¥é™¢æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "开始入院日期")
    private Date startInHospTime;
    /**
     * ç»“束入院日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "结束入院日期")
    private Date endInHospTime;
    @ApiModelProperty(value = "是否外部导入:0不是    1是")
    private Integer outImport = 0;
    @ApiModelProperty(value = "查询类型:1全部   2出院   3入院   4门诊   5外部患者 ")
    private Integer queryType = 1;
    @ApiModelProperty(value = "机构ID")
    private String guid;
    @ApiModelProperty(value = "操作人")
    private String operator;
    @ApiModelProperty(value = "操作人ID")
    private String operatorId;
    @ApiModelProperty(value = "标签名")
    private String tagname;
    @ApiModelProperty(value = "随访标识:0不需要,1要")
    private String fuflag;
    @ApiModelProperty(value = "随访日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date fudate;
    @ApiModelProperty(value = "随访周期建议")
    private String fuperiod;
    @ApiModelProperty(value = "随访类型编码")
    private String futypecode;
    @ApiModelProperty(value = "随访类型名称")
    private String futypedesc;
    @ApiModelProperty(value = "随访复诊指导")
    private String fuadvice;
    @ApiModelProperty(value = "专病随访建议")
    private String fuspecialadvice;
    /**
     * è¯ä»¶å·ç 
     */
    @ApiModelProperty("证件号码")
    @Excel(name = " è¯ä»¶å·ç  ")
    private String idcardno;
    /**
     * ç»ç®¡åŒ»ç”Ÿ
     */
    @ApiModelProperty("经管医生")
    @Excel(name = " ç»ç®¡åŒ»ç”Ÿ ")
    private String managementDoctor;
    /**
     * ç»ç®¡åŒ»ç”Ÿ
     */
    @ApiModelProperty("经管医生")
    @Excel(name = " ç»ç®¡åŒ»ç”Ÿ ")
    private String managementDoctorCode;
    @ApiModelProperty(value = "最后开始时间")
    private String lastStartTime;
    @ApiModelProperty(value = "最后结束时间")
    private String lastEndTime;
    /**
     * æœºæž„ID
     */
    @ApiModelProperty("院区")
    @Excel(name = " é™¢åŒº ")
    private String campusid;
    /**
     * å°±è¯Šæ¥æºID
     */
    @ApiModelProperty("就诊来源ID")
    @Excel(name = " å°±è¯Šæ¥æºID ")
    private String encounterSourceId;
    /**
     * é‡è¦æ€§æ ‡è®°ç¼–码
     * æžšä¸¾ç±»åž‹ï¼šImportFlagEnum
     * FH0165.98 - ç‰¹çº¦äººå‘˜
     * FH0165.99 - ç¦»ä¼‘人员
     * FH0165.03 - æ— 
     * FH0165.02 - æ—¥é—´è¯Šç–—
     * FH0165.01 - æ—¥é—´æ‰‹æœ¯
     */
    @ApiModelProperty(value = "重要性标记编码", example = "FH0165.01")
    @Excel(name = " é‡è¦æ€§æ ‡è®°ç¼–码 ")
    private String importanceFlagCode;
    /**
     * ç´§æ€¥ç¨‹åº¦ç¼–码
     * æžšä¸¾ç±»åž‹ï¼šEmergencyLevelEnum
     * FH0164.01 - ç›®æ ‡
     * FH0164.02 - æ€¥å±é‡
     * FH0164.03 - æ‹©æœŸ
     * FH0164.04 - åŸºç¡€
     */
    @ApiModelProperty(value = "紧急程度编码", example = "FH0164.01")
    @Excel(name = " ç´§æ€¥ç¨‹åº¦ç¼–码 ")
    private String emergencyLevelCode;
    /**
     * '满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2)
     */
    @ApiModelProperty("'满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2)")
    @Excel(name = " '满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2) ")
    private String inhospMydFlag;
    /**
     * è¯å“ç¼–码,多个用,分割
     */
    @ApiModelProperty("药品编码,多个用,分割")
    @Excel(name = "药品编码,多个用,分割")
    private String medicalCode;
    /**
     * è¯å“åç§°,多个用,分割
     */
    @ApiModelProperty("药品名称,多个用,分割")
    @Excel(name = "药品名称,多个用,分割")
    private String medicalName;
    /**
     * è¯å“å…¬å¸,多个用,分割
     */
    @ApiModelProperty("药品公司,多个用,分割")
    @Excel(name = "药品公司,多个用,分割")
    private String medicalCompany;
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/PatMedInhospDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,565 @@
package com.ruoyi.inhospvisit.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.util.Date;
import java.util.List;
/**
 * æ‚£è€…住院记录对象返参 pat_med_inhosp
 *
 * @author smartor
 * @date 2023-03-04
 */
@ApiModel(value = "PatMedInhospDTO", description = "患者住院记录对象返参")
@Data
public class PatMedInhospDTO extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty(value = "自增ID")
    private Long inhospid;
    /**
     * æ€§åˆ«
     */
    @ApiModelProperty(value = "性别 1男  2女")
    private Long sex;
    /**
     * ä½é™¢å·
     */
    @ApiModelProperty(value = "住院号")
    private String inhospno;
    /**
     * æµæ°´å·
     */
    @ApiModelProperty(value = "流水号")
    private String serialnum;
    /**
     * æŠ¤å£«ID
     */
    @ApiModelProperty(value = "护士ID")
    private String nurseId;
    /**
     * æŠ¤å£«å§“名
     */
    @ApiModelProperty(value = "护士姓名")
    private String nurseName;
    /**
     * åŒ»é™¢åç§°
     */
    @ApiModelProperty(value = "医院名称")
    @Excel(name = " åŒ»é™¢åç§° ")
    private String hospitalname;
    /**
     * åŒ»é™¢ç¼–号
     */
    @ApiModelProperty(value = "医院编号")
    private String hospitalcode;
    /**
     * é™¢åŒºç¼–号
     */
    @ApiModelProperty(value = "院区编号")
    private String hospitaldistrictcode;
    /**
     * é™¢åŒºç¼–号集合
     */
    @ApiModelProperty(value = "院区编号集合")
    private List<String> hospitaldistrictcodeList;
    /**
     * é™¢åŒºåç§°
     */
    @ApiModelProperty(value = "院区名称")
    @Excel(name = " é™¢åŒºåç§° ")
    private String hospitaldistrictname;
    /**
     * å…¥é™¢è¯Šæ–­ICD值
     */
    @ApiModelProperty(value = "入院诊断ICD值")
    private String icd10code;
    /**
     * å…¥é™¢è¯Šæ–­åç§°
     */
    @ApiModelProperty(value = "入院诊断名称")
    private String diagname;
    /**
     * å…¥é™¢æ—¶é—´
     */
    @ApiModelProperty(value = "入院时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = " å…¥é™¢æ—¶é—´ ", width = 30, dateFormat = "yyyy-MM-dd")
    private Date starttime;
    /**
     * å‡ºé™¢æ—¶é—´
     */
    @ApiModelProperty(value = "出院时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = " å‡ºé™¢æ—¶é—´ ", width = 30, dateFormat = "yyyy-MM-dd")
    private Date endtime;
    /**
     * å…¥é™¢ç§‘室代码
     */
    @ApiModelProperty(value = "入院科室代码")
    private String deptcode;
    /**
     * å…¥é™¢ç§‘室代码
     */
    @ApiModelProperty(value = "入院科室代码集合")
    private List<String> deptcodeList;
    /**
     * å…¥é™¢ç§‘室名称
     */
    @ApiModelProperty(value = "入院科室名称")
    private String deptname;
    /**
     * æˆ¿é—´å·
     */
    @ApiModelProperty(value = "房间号")
    private String roomno;
    /**
     * åºŠä½å·
     */
    @ApiModelProperty(value = "床位号")
    @Excel(name = " åºŠä½å· ")
    private String bedNo;
    /**
     * æœºæž„ID
     */
    @ApiModelProperty(value = "机构ID")
    @Excel(name = " æœºæž„ID ")
    private String orgid;
    /**
     * åˆ é™¤æ ‡è®°
     */
    @ApiModelProperty(value = "删除标记")
    private String delFlag;
    /**
     * ä¸Šä¼ æ ‡è®°
     */
    @ApiModelProperty(value = "上传标记")
    private Long isupload;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    @ApiModelProperty(value = "上传时间")
    private Date uploadTime;
    /**
     * æ¡£æ¡ˆID
     */
    @ApiModelProperty(value = "档案ID")
    private Long patid;
    /**
     * å‡ºé™¢è¯Šæ–­
     */
    @ApiModelProperty(value = "出院诊断")
    @Excel(name = " å‡ºé™¢è¯Šæ–­ ")
    private String leavediagname;
    /**
     * å‡ºé™¢è¯Šæ–­ICD值
     */
    @ApiModelProperty(value = "出院诊断ICD值")
    private String leaveicd10code;
    /**
     * åŒ»ç”Ÿå·¥å·
     */
    @ApiModelProperty(value = "医生工号")
    private String drcode;
    /**
     * åŒ»ç”Ÿåç§°
     */
    @ApiModelProperty(value = "医生名称")
    @Excel(name = " åŒ»ç”Ÿåç§° ")
    private String drname;
    /**
     * æ˜¯å¦ç”Ÿæˆæ–¹æ¡ˆçŠ¶æ€;0预入院 1生成入院 2生成出院 9无匹配方案   3生成入院撤销   4生成出院撤销
     */
    @ApiModelProperty(value = "是否生成方案状态;0预入院 1生成入院 2生成出院 9无匹配方案   3生成入院撤销   4生成出院撤销")
    private Long schemestatus;
    /**
     * æ˜¯å¦ç”Ÿæˆé€šç”¨æ–¹æ¡ˆçŠ¶æ€;0未生成 1生成 9无匹配方案
     */
    @ApiModelProperty(value = "是否生成通用方案状态;0未生成 1生成 9无匹配方案")
    private Long generalschemestatus;
    /**
     * å‡ºé™¢ç§‘室代码
     */
    @ApiModelProperty(value = "出院科室代码")
    private String leaveldeptcode;
    /**
     * å‡ºé™¢ç§‘室名称
     */
    @ApiModelProperty(value = "出院科室名称")
    @Excel(name = " å‡ºé™¢ç§‘室名称 ")
    private String leaveldeptname;
    /**
     * ç—…区ID
     */
    @ApiModelProperty(value = "病区ID")
    private String hospitaldistrictid;
    /**
     * å‡ºé™¢ç—…区编号
     */
    @ApiModelProperty(value = "出院病区编号")
    private String leavehospitaldistrictcode;
    /**
     * å‡ºé™¢ç—…区名称
     */
    @ApiModelProperty(value = "出院病区名称")
    @Excel(name = " å‡ºé™¢ç—…区名称 ")
    private String leavehospitaldistrictname;
    /**
     * å‡ºé™¢ç—…区ID
     */
    @ApiModelProperty(value = "出院病区ID")
    private String leavehospitaldistrictid;
    /**
     * å…¥é™¢ç§‘室ID
     */
    @ApiModelProperty(value = "入院科室ID")
    private String deptid;
    /**
     * å‡ºé™¢ç§‘室ID
     */
    @ApiModelProperty(value = "出院科室ID")
    private String leaveldeptid;
    /**
     * æ–¹æ¡ˆçŠ¶æ€æ“ä½œæ—¶é—´
     */
    @ApiModelProperty(value = "方案状态操作时间")
    private Date schemetime;
    /**
     * æ‚£è€…姓名
     */
    @ApiModelProperty(value = "患者姓名")
    private String patname;
    /**
     * æ‚£è€…编号
     */
    @ApiModelProperty(value = "患者编号")
    private String patno;
    /**
     * ä½é™¢æ ‡è¯†ï¼š 0:在院  1:出院    2:转科
     */
    @ApiModelProperty(value = "住院标识: 0:在院  1:出院    2:转科  3待入院")
    private String inhospstate;
    /**
     * ç§‘室长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String deptcheckFlag;
    /**
     * ç—…区长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "病区长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String wardcheckFlag;
    /**
     * ç–¾ç—…长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "疾病长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String diagcheckFlag;
    /**
     * å…¥é™¢ç§‘室长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "入院科室长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String inDeptcheckFlag;
    /**
     * å…¥é™¢ç—…区长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "入院病区长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String inWardcheckFlag;
    /**
     * å¹´é¾„
     */
    @ApiModelProperty(value = "年龄")
    private String age;
    /**
     * æ‰‹æœºå·
     */
    @ApiModelProperty(value = "手机号")
    private String telcode;
    /**
     * é•¿æœŸä»»åŠ¡åŽŸå› 
     */
    @ApiModelProperty(value = "长期任务原因")
    private String longTaskReason;
    /**
     * pageNum
     */
    @ApiModelProperty("pageNum")
    private Integer pageNum;
    /**
     * pageSize
     */
    @ApiModelProperty("pageSize")
    private Integer pageSize;
    /**
     * pageNum
     */
    @ApiModelProperty("pn")
    private Integer pn;
    /**
     * pageSize
     */
    @ApiModelProperty("ps")
    private Integer ps;
    /**
     * å‡ºé™¢æ–¹å¼åºå·
     */
    @ApiModelProperty("出院方式序号")
    @Excel(name = "出院方式序号")
    private String outWayId;
    /**
     * å‡ºé™¢æ–¹å¼åç§°
     */
    @ApiModelProperty("出院方式名称")
    @Excel(name = "出院方式名称")
    private String outWayName;
    /**
     * å‡ºå…¥é™¢æ ‡è¯†
     */
    @ApiModelProperty("出入院标识:0入院  1出院 2 è½¬ç§‘  3 é¢„入院")
    private Integer cry;
    /**
     * å‡ºé™¢ç—…区编号
     */
    @ApiModelProperty(value = "出院病区编号集合")
    private List<String> leavehospitaldistrictcodes;
    /**
     * å‡ºé™¢ç§‘室代码集合
     */
    @ApiModelProperty(value = "出院科室代码集合")
    private List<String> leaveldeptcodes;
    /**
     * å¼€å§‹å‡ºé™¢æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "开始出院日期")
    private Date startOutHospTime;
    /**
     * ç»“束出院日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "结束出院日期")
    private Date endOutHospTime;
    /**
     * å¼€å§‹å…¥é™¢æ—¥æœŸ
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "开始入院日期")
    private Date startInHospTime;
    /**
     * ç»“束入院日期
     */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "结束入院日期")
    private Date endInHospTime;
    @ApiModelProperty(value = "是否外部导入:0不是    1是")
    private Integer outImport = 0;
    @ApiModelProperty(value = "查询类型:1全部   2出院   3入院   4门诊   5外部患者 ")
    private Integer queryType = 1;
    @ApiModelProperty(value = "机构ID")
    private String guid;
    @ApiModelProperty(value = "操作人")
    private String operator;
    @ApiModelProperty(value = "操作人ID")
    private String operatorId;
    @ApiModelProperty(value = "标签名")
    private String tagname;
    @ApiModelProperty(value = "随访标识:0不需要,1要")
    private String fuflag;
    @ApiModelProperty(value = "随访日期")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date fudate;
    @ApiModelProperty(value = "随访周期建议")
    private String fuperiod;
    @ApiModelProperty(value = "随访类型编码")
    private String futypecode;
    @ApiModelProperty(value = "随访类型名称")
    private String futypedesc;
    @ApiModelProperty(value = "随访复诊指导")
    private String fuadvice;
    @ApiModelProperty(value = "专病随访建议")
    private String fuspecialadvice;
    /**
     * è¯ä»¶å·ç 
     */
    @ApiModelProperty("证件号码")
    @Excel(name = " è¯ä»¶å·ç  ")
    private String idcardno;
    /**
     * ç»ç®¡åŒ»ç”Ÿ
     */
    @ApiModelProperty("经管医生")
    @Excel(name = " ç»ç®¡åŒ»ç”Ÿ ")
    private String managementDoctor;
    /**
     * ç»ç®¡åŒ»ç”Ÿ
     */
    @ApiModelProperty("经管医生")
    @Excel(name = " ç»ç®¡åŒ»ç”Ÿ ")
    private String managementDoctorCode;
    @ApiModelProperty(value = "最后开始时间")
    private String lastStartTime;
    @ApiModelProperty(value = "最后结束时间")
    private String lastEndTime;
    /**
     * æœºæž„ID
     */
    @ApiModelProperty("院区")
    @Excel(name = " é™¢åŒº ")
    private String campusid;
    /**
     * å°±è¯Šæ¥æºID
     */
    @ApiModelProperty("就诊来源ID")
    @Excel(name = " å°±è¯Šæ¥æºID ")
    private String encounterSourceId;
    /**
     * é‡è¦æ€§æ ‡è®°ç¼–码
     * æžšä¸¾ç±»åž‹ï¼šImportFlagEnum
     * FH0165.98 - ç‰¹çº¦äººå‘˜
     * FH0165.99 - ç¦»ä¼‘人员
     * FH0165.03 - æ— 
     * FH0165.02 - æ—¥é—´è¯Šç–—
     * FH0165.01 - æ—¥é—´æ‰‹æœ¯
     */
    @ApiModelProperty(value = "重要性标记编码", example = "FH0165.01")
    @Excel(name = " é‡è¦æ€§æ ‡è®°ç¼–码 ")
    private String importanceFlagCode;
    /**
     * ç´§æ€¥ç¨‹åº¦ç¼–码
     * æžšä¸¾ç±»åž‹ï¼šEmergencyLevelEnum
     * FH0164.01 - ç›®æ ‡
     * FH0164.02 - æ€¥å±é‡
     * FH0164.03 - æ‹©æœŸ
     * FH0164.04 - åŸºç¡€
     */
    @ApiModelProperty(value = "紧急程度编码", example = "FH0164.01")
    @Excel(name = " ç´§æ€¥ç¨‹åº¦ç¼–码 ")
    private String emergencyLevelCode;
    @ApiModelProperty(value = "是否宣教过")
    @Excel(name = " æ˜¯å¦å®£æ•™å®Œæˆ:0 æœªå®£æ•™ 1 å·²å®£æ•™ ")
    private String finishFlag = "0";
    /**
     * '满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2)
     */
    @ApiModelProperty("'满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2)")
    @Excel(name = " '满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2) ")
    private String inhospMydFlag;
    /**
     * è¯å“ç¼–码,多个用,分割
     */
    @ApiModelProperty("药品编码,多个用,分割")
    @Excel(name = "药品编码,多个用,分割")
    private String medicalCode;
    /**
     * è¯å“åç§°,多个用,分割
     */
    @ApiModelProperty("药品名称,多个用,分割")
    @Excel(name = "药品名称,多个用,分割")
    private String medicalName;
    /**
     * è¯å“å…¬å¸,多个用,分割
     */
    @ApiModelProperty("药品公司,多个用,分割")
    @Excel(name = "药品公司,多个用,分割")
    private String medicalCompany;
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/PatMedInhospVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,461 @@
package com.ruoyi.inhospvisit.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.util.Date;
import java.util.List;
/**
 * æ‚£è€…住院记录对象 pat_med_inhosp
 *
 * @author smartor
 * @date 2023-03-04
 */
@ApiModel(value = "PatMedInhosp", description = "患者住院记录对象")
@Data
public class PatMedInhospVO extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty(value = "自增ID")
    private Long inhospid;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty(value = "自增ID")
    private String inhospno;
    /**
     * éšè®¿æ ‡è¯†ï¼š0不需要,1需要
     */
    @ApiModelProperty(value = "随访标识:0不需要,1需要")
    private String fuflag;
    /**
     * ç–¾ç—…处理:0未处理   1处理成功   2处理失败
     */
    @ApiModelProperty(value = "疾病处理:0未处理   1处理成功   2处理失败")
    private String diagcheckFlag;
    /**
     *病区处理:0未处理   1处理成功   2处理失败
     */
    @ApiModelProperty(value = "病区处理:0未处理   1处理成功   2处理失败")
    private String wardcheckFlag;
    /**
     * éƒ¨é—¨å¤„理:0未处理   1处理成功   2处理失败
     */
    @ApiModelProperty(value = "部门处理:0未处理   1处理成功   2处理失败")
    private String deptcheckFlag;
    /**
     * å…¥é™¢ç§‘室长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "入院科室长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String  inDeptcheckFlag;
    /**
     * å…¥é™¢ç—…区长期任务,是否处理:0未处理   1处理
     */
    @ApiModelProperty(value = "入院病区长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String inWardcheckFlag;
    /**
     * æŠ¤å£«ID
     */
    @ApiModelProperty(value = "护士ID")
    private String nurseId;
    /**
     * æŠ¤å£«ID
     */
    @ApiModelProperty(value = "操作人ID")
    private String operatorId;
    @ApiModelProperty(value = "操作人")
    private String operator;
    @ApiModelProperty(value = "机构ID")
    private String guid;
    /**
     * æŠ¤å£«å§“名
     */
    @ApiModelProperty(value = "护士姓名")
    private String nurseName;
    /**
     * æµæ°´å·
     */
    @ApiModelProperty(value = "流水号")
    private String serialnum;
    /**
     * åŒ»é™¢åç§°
     */
    @ApiModelProperty(value = "医院名称")
    @Excel(name = " åŒ»é™¢åç§° ")
    private String hospitalname;
    /**
     * åŒ»é™¢ç¼–号
     */
    @ApiModelProperty(value = "医院编号")
    private String hospitalcode;
    /**
     * é™¢åŒºç¼–号
     */
    @ApiModelProperty(value = "院区编号")
    private String hospitaldistrictcode;
    /**
     * é™¢åŒºåç§°
     */
    @ApiModelProperty(value = "院区名称")
    @Excel(name = " é™¢åŒºåç§° ")
    private String hospitaldistrictname;
    /**
     * å…¥é™¢è¯Šæ–­ICD值
     */
    @ApiModelProperty(value = "入院诊断ICD值")
    private String icd10code;
    /**
     * å…¥é™¢è¯Šæ–­åç§°
     */
    @ApiModelProperty(value = "入院诊断名称")
    private String diagname;
    /**
     * å…¥é™¢æ—¶é—´
     */
    @ApiModelProperty(value = "入院时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = " å…¥é™¢æ—¶é—´ ", width = 30, dateFormat = "yyyy-MM-dd")
    private Date starttime;
    /**
     * å‡ºé™¢æ—¶é—´
     */
    @ApiModelProperty(value = "出院时间")
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = " å‡ºé™¢æ—¶é—´ ", width = 30, dateFormat = "yyyy-MM-dd")
    private Date endtime;
    /**
     * å…¥é™¢ç§‘室代码
     */
    @ApiModelProperty(value = "入院科室代码")
    private String deptcode;
    /**
     * å…¥é™¢ç§‘室名称
     */
    @ApiModelProperty(value = "入院科室名称")
    private String deptname;
    /**
     * æˆ¿é—´å·
     */
    @ApiModelProperty(value = "房间号")
    private String roomno;
    /**
     * åºŠä½å·
     */
    @ApiModelProperty(value = "床位号")
    @Excel(name = " åºŠä½å· ")
    private String bedNo;
    /**
     * æœºæž„ID
     */
    @ApiModelProperty(value = "机构ID")
    @Excel(name = " æœºæž„ID ")
    private String orgid;
    /**
     * åˆ é™¤æ ‡è®°
     */
    @ApiModelProperty(value = "删除标记")
    private String delFlag;
    /**
     * ä¸Šä¼ æ ‡è®°
     */
    @ApiModelProperty(value = "上传标记")
    private Long isupload;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    @ApiModelProperty(value = "上传时间")
    private Date uploadTime;
    /**
     * æ¡£æ¡ˆID
     */
    @ApiModelProperty(value = "档案ID")
    private Long patid;
    /**
     * å‡ºé™¢è¯Šæ–­
     */
    @ApiModelProperty(value = "出院诊断")
    @Excel(name = " å‡ºé™¢è¯Šæ–­ ")
    private String leavediagname;
    /**
     * å‡ºé™¢è¯Šæ–­ICD值
     */
    @ApiModelProperty(value = "出院诊断ICD值")
    private String leaveicd10code;
    /**
     * åŒ»ç”Ÿå·¥å·
     */
    @ApiModelProperty(value = "医生工号")
    private String drcode;
    /**
     * åŒ»ç”Ÿåç§°
     */
    @ApiModelProperty(value = "医生名称")
    @Excel(name = " åŒ»ç”Ÿåç§° ")
    private String drname;
    /**
     * æ˜¯å¦ç”Ÿæˆæ–¹æ¡ˆçŠ¶æ€;0未生成 1生成入院 2生成出院 9无匹配方案
     */
    @ApiModelProperty(value = "是否生成方案状态;0未生成 1生成入院 2生成出院 9无匹配方案")
    private Long schemestatus;
    /**
     * æ˜¯å¦ç”Ÿæˆé€šç”¨æ–¹æ¡ˆçŠ¶æ€;0未生成 1生成 9无匹配方案
     */
    @ApiModelProperty(value = "是否生成通用方案状态;0未生成 1生成 9无匹配方案")
    private Long generalschemestatus;
    /**
     * å‡ºé™¢ç§‘室代码集合
     */
    @ApiModelProperty(value = "出院科室代码集合")
    private List<String> leaveldeptcodes;
    /**
     * å‡ºé™¢ç§‘室名称
     */
    @ApiModelProperty(value = "出院科室名称")
    @Excel(name = " å‡ºé™¢ç§‘室名称 ")
    private String leaveldeptname;
    /**
     * ç—…区ID
     */
    @ApiModelProperty(value = "病区ID")
    private String hospitaldistrictid;
    /**
     * å‡ºé™¢ç—…区编号
     */
    @ApiModelProperty(value = "出院病区编号集合")
    private List<String> leavehospitaldistrictcodes;
    /**
     * å‡ºé™¢ç—…区名称
     */
    @ApiModelProperty(value = "出院病区名称")
    @Excel(name = " å‡ºé™¢ç—…区名称 ")
    private String leavehospitaldistrictname;
    /**
     * å‡ºé™¢ç—…区ID
     */
    @ApiModelProperty(value = "出院病区ID")
    private String leavehospitaldistrictid;
    /**
     * å…¥é™¢ç§‘室ID
     */
    @ApiModelProperty(value = "入院科室ID")
    private String deptid;
    /**
     * å‡ºé™¢ç§‘室ID
     */
    @ApiModelProperty(value = "出院科室ID")
    private String leaveldeptid;
    /**
     * æ–¹æ¡ˆçŠ¶æ€æ“ä½œæ—¶é—´
     */
    @ApiModelProperty(value = "方案状态操作时间")
    private Date schemetime;
    /**
     * æ‚£è€…姓名
     */
    @ApiModelProperty(value = "患者姓名")
    private String patname;
    /**
     * æ‚£è€…编号
     */
    @ApiModelProperty(value = "患者编号")
    private String patno;
    /**
     * ä½é™¢æ ‡è¯†ï¼š 0:在院  1:出院    2:转科
     */
    @ApiModelProperty(value = "住院标识: 0:在院  1:出院    2:转科")
    private String inhospstate;
    /**
     * é•¿æœŸä»»åŠ¡ï¼Œæ˜¯å¦å¤„ç†ï¼š0未处理   1处理
     */
    @ApiModelProperty(value = "长期任务,是否处理:0未处理   1处理")
    private String checkFlag;
    /**
     * å¹´é¾„
     */
    @ApiModelProperty(value = "年龄")
    private String age;
    /**
     * æ‰‹æœºå·
     */
    @ApiModelProperty(value = "手机号")
    private String telcode;
    /**
     * pageNum
     */
    @ApiModelProperty("pageNum")
    private Integer pageNum;
    /**
     * pageSize
     */
    @ApiModelProperty("pageSize")
    private Integer pageSize;
    /**
     * å¼€å§‹å‡ºé™¢æ—¥æœŸ
     */
    @ApiModelProperty(value = "开始出院日期")
    private Date startOutHospTime;
    /**
     * ç»“束出院日期
     */
    @ApiModelProperty(value = "结束出院日期")
    private Date endOutHospTime;
    /**
     * å¼€å§‹å…¥é™¢æ—¥æœŸ
     */
    @ApiModelProperty(value = "开始入院日期")
    private Date startInHospTime;
    /**
     * ç»“束入院日期
     */
    @ApiModelProperty(value = "结束入院日期")
    private Date endInHospTime;
    /**
     * ç»ç®¡åŒ»ç”Ÿ
     */
    @ApiModelProperty("经管医生")
    @Excel(name = " ç»ç®¡åŒ»ç”Ÿ ")
    private String managementDoctor;
    /**
     * ç»ç®¡åŒ»ç”Ÿ
     */
    @ApiModelProperty("经管医生")
    @Excel(name = " ç»ç®¡åŒ»ç”Ÿ ")
    private String managementDoctorCode;
    /**
     * æœºæž„ID
     */
    @ApiModelProperty("院区")
    @Excel(name = " é™¢åŒº ")
    private String campusid;
    /**
     * å°±è¯Šæ¥æºID
     */
    @ApiModelProperty("就诊来源ID")
    @Excel(name = " å°±è¯Šæ¥æºID ")
    private String encounterSourceId;
    /**
     * é‡è¦æ€§æ ‡è®°ç¼–码
     * æžšä¸¾ç±»åž‹ï¼šImportFlagEnum
     * FH0165.98 - ç‰¹çº¦äººå‘˜
     * FH0165.99 - ç¦»ä¼‘人员
     * FH0165.03 - æ— 
     * FH0165.02 - æ—¥é—´è¯Šç–—
     * FH0165.01 - æ—¥é—´æ‰‹æœ¯
     */
    @ApiModelProperty(value = "重要性标记编码", example = "FH0165.01")
    @Excel(name = " é‡è¦æ€§æ ‡è®°ç¼–码 ")
    private String importanceFlagCode;
    /**
     * ç´§æ€¥ç¨‹åº¦ç¼–码
     * æžšä¸¾ç±»åž‹ï¼šEmergencyLevelEnum
     * FH0164.01 - ç›®æ ‡
     * FH0164.02 - æ€¥å±é‡
     * FH0164.03 - æ‹©æœŸ
     * FH0164.04 - åŸºç¡€
     */
    @ApiModelProperty(value = "紧急程度编码", example = "FH0164.01")
    @Excel(name = " ç´§æ€¥ç¨‹åº¦ç¼–码 ")
    private String emergencyLevelCode;
    /**
     * '满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2)
     */
    @ApiModelProperty("'满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2)")
    @Excel(name = " '满意度标识:0未做满度   1住院满度度  2出院满意度  ï¼ˆå¦‚果既要做住院,又要做出院,则用“,”分割,例如1,2) ")
    private String inhospMydFlag;
    /**
     * è¯å“ç¼–码,多个用,分割
     */
    @ApiModelProperty("药品编码,多个用,分割")
    @Excel(name = "药品编码,多个用,分割")
    private String medicalCode;
    /**
     * è¯å“åç§°,多个用,分割
     */
    @ApiModelProperty("药品名称,多个用,分割")
    @Excel(name = "药品名称,多个用,分割")
    private String medicalName;
    /**
     * è¯å“å…¬å¸,多个用,分割
     */
    @ApiModelProperty("药品公司,多个用,分割")
    @Excel(name = "药品公司,多个用,分割")
    private String medicalCompany;
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitInhospRow.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,572 @@
package com.ruoyi.inhospvisit.domain.row;
import java.io.Serializable;
import java.util.Date;
/**
 * {@code pat_med_inhosp} çš„随访侧只读行。
 *
 * <h3>⚠️ ä¸Žæœ¬æ¨¡å—çš„ SQL é‡å¤é—®é¢˜ï¼ˆå¿…读)</h3>
 * åŒä¸€å¼ è¡¨åœ¨ smartor çš„ {@code PatMedInhospMapper} ä¹Ÿæœ‰ä¸€ä»½æŸ¥è¯¢ã€‚这里<b>刻意各自持有一份</b>,
 * ç†ç”±ï¼š
 * <ul>
 *   <li>本模块的 {@code pom.xml} åªä¾èµ– ruoyi-common(不依赖 smartor,否则循环依赖),
 *       è€Œ smartor åè¿‡æ¥è¦ä¾èµ–本模块,所以只能自己查;</li>
 *   <li>纪律:<b>两边的 SQL å¿…须逐条等价</b>。由
 *       {@code sql/_probe/verify_inhospvisit_mapper.py}(接口↔XML ä¸€è‡´æ€§ï¼‰ä¸Ž
 *       {@code sql/_probe/verify_inhospvisit_sql_expansion.py}(展开后逐条比对,AND é¡ºåºæ— å…³ï¼‰
 *       åŒå‘把关。改任一边都必须同步改另一边并重跑这两个探针。</li>
 *   <li>另一种做法是把查询"包一层端口"委托回 smartor,但那样本模块就只剩编排逻辑、
 *       CRUD å…¨é å›žè°ƒï¼Œåè€Œæ›´ç¢Žã€‚这里选择了"重复但可验证"。</li>
 * </ul>
 *
 * <h3>字段来源(@ b25c3b51)</h3>
 * å­—段集是按 <b>{@code boxedServiceSubtask} ä¸Žç¼–排层的真实 getter è®¿é—®</b>倒推出来的:
 * <ul>
 *   <li>盒子组装用 24 ä¸ªï¼š{@code Campusid Deptcode Deptname Diagname Drcode Drname Endtime Fudate
 *       Hospitaldistrictcode Hospitaldistrictname Inhospid Leavediagname Leavehospitaldistrictcode
 *       Leavehospitaldistrictname Leaveicd10code Leaveldeptcode Leaveldeptname ManagementDoctor
 *       ManagementDoctorCode NurseId NurseName Orgid Remark Starttime};</li>
 *   <li>扫描/停机用:{@code deptcheck_flag wardcheck_flag diagcheck_flag schedulecheck_flag
 *       in_deptcheck_flag in_wardcheck_flag inhospstate fuflag patid long_task_reason};</li>
 *   <li>日志用:{@code patname patno serialnum medical_code}。</li>
 * </ul>
 * <b>少一个字段 = å¯¹åº”çš„ setter é™é»˜ä¸ç”Ÿæ•ˆ</b>(XML é‡Œæ˜¯ {@code <if test="xxx != null">}),
 * æ‰€ä»¥è¿™é‡Œä¸åš"精简",宁可多带几个。
 */
public class VisitInhospRow implements Serializable {
    private static final long serialVersionUID = 1L;
    // ==================== ä¸»é”®ä¸Žå½’属 ====================
    /** å‡ºå…¥é™¢è®°å½•ID */
    private Long inhospid;
    /** æ‚£è€…档案ID(Long,与 PatArchive.id ä¸€è‡´ï¼‰ */
    private Long patid;
    /** æœºæž„ID */
    private String orgid;
    /** é™¢åŒºID */
    private String campusid;
    /** æ‚£è€…编号 */
    private String patno;
    /** ä½é™¢æµæ°´å· */
    private String serialnum;
    /** æ‚£è€…姓名(仅日志用) */
    private String patname;
    /** ä½é™¢å·ï¼ˆåŽŸ inhospno)—— ä»…日志/排查用 */
    private String inhospno;
    /**
     * æ‚£è€…年龄。
     * <p>
     * âš ï¸ æ¥æºæ˜¯ JOIN å‡ºæ¥çš„ {@code pat_archive.age}(原 SQL å†™çš„ {@code a.age AS age}),
     * ç±»åž‹ {@code Long};{@code selectByInhospid} é‚£æ¡å•表查询<b>不带</b> age(与原实现一致),
     * æ‰€ä»¥æŒ‰ä¸»é”®æŸ¥å‡ºæ¥çš„行里这个字段是 null。
     */
    private Long age;
    /** æ‚£è€…性别(来源 {@code pat_archive.sex},原 SQL æ˜¯ {@code a.sex}) */
    private Long sex;
    /** æ‚£è€…手机号(来源 {@code pat_archive.telcode},原 SQL æ˜¯ {@code a.telcode as telcode}) */
    private String telcode;
    // ==================== æ—¶é—´ ====================
    /** å…¥é™¢æ—¶é—´ */
    private Date starttime;
    /** å‡ºé™¢æ—¶é—´ */
    private Date endtime;
    /** HIS ç»™çš„随访时间 */
    private Date fudate;
    // ==================== å…¥é™¢ç»´åº¦ ====================
    /** å…¥é™¢ç§‘室编码 */
    private String deptcode;
    /** å…¥é™¢ç§‘室名称 */
    private String deptname;
    /** å…¥é™¢ç—…区编码 */
    private String hospitaldistrictcode;
    /** å…¥é™¢ç—…区名称 */
    private String hospitaldistrictname;
    // ==================== ç¦»é™¢ç»´åº¦ ====================
    /** ç¦»é™¢ç§‘室编码 */
    private String leaveldeptcode;
    /** ç¦»é™¢ç§‘室名称 */
    private String leaveldeptname;
    /** ç¦»é™¢ç—…区编码 */
    private String leavehospitaldistrictcode;
    /** ç¦»é™¢ç—…区名称 */
    private String leavehospitaldistrictname;
    // ==================== è¯Šæ–­ ====================
    /** ç¦»é™¢ ICD10 ç¼–码 */
    private String leaveicd10code;
    /** ç¦»é™¢è¯Šæ–­åç§° */
    private String leavediagname;
    /** è¯Šæ–­åç§° */
    private String diagname;
    // ==================== åŒ»æŠ¤ ====================
    /** è´£ä»»æŠ¤å£«ID */
    private String nurseId;
    /** è´£ä»»æŠ¤å£«å§“名 */
    private String nurseName;
    /** åŒ»ç”Ÿç¼–码 */
    private String drcode;
    /** åŒ»ç”Ÿå§“名 */
    private String drname;
    /** ç®¡ç†åŒ»ç”Ÿ */
    private String managementDoctor;
    /** ç®¡ç†åŒ»ç”Ÿç¼–码 */
    private String managementDoctorCode;
    // ==================== éšè®¿çŠ¶æ€æ ‡å¿—ï¼ˆéšè®¿çŠ¶æ€æœºæœ¬ä½“ï¼‰ ====================
    /** ç¦»é™¢ç§‘室维度是否已处理:0=未处理 1=已处理 2=跳过/失败 */
    private String deptcheckFlag;
    /** ç¦»é™¢ç—…区维度是否已处理 */
    private String wardcheckFlag;
    /** ç–¾ç—…维度是否已处理 */
    private String diagcheckFlag;
    /** ä»»åŠ¡ç»„æ˜¯å¦å·²çº³å…¥ï¼ˆç‹¬ç«‹äºŽä¸Šé¢ä¸‰ä¸ªç»´åº¦ï¼‰ */
    private String scheduleCheckFlag;
    /** å…¥é™¢ç§‘室宣教维度是否已处理 */
    private String inDeptcheckFlag;
    /** å…¥é™¢ç—…区宣教维度是否已处理 */
    private String inWardcheckFlag;
    /** åœ¨é™¢çŠ¶æ€ï¼š0=在院 1=已出院 2=转科 3=预入院 */
    private String inhospstate;
    /** æ˜¯å¦éœ€è¦éšè®¿ï¼š1=需要 */
    private String fuflag;
    /** æœªç”Ÿæˆä»»åŠ¡çš„åŽŸå› ç´¯ç§¯ */
    private String longTaskReason;
    // ==================== å…¶å®ƒ ====================
    /** å¤‡æ³¨ */
    private String remark;
    /** è¯å“ç¼–码串(仅 sltd è¯å“å®£æ•™ç”¨ï¼‰ */
    private String medicalCode;
    public Long getInhospid() {
        return inhospid;
    }
    public void setInhospid(Long inhospid) {
        this.inhospid = inhospid;
    }
    public Long getPatid() {
        return patid;
    }
    public void setPatid(Long patid) {
        this.patid = patid;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getCampusid() {
        return campusid;
    }
    public void setCampusid(String campusid) {
        this.campusid = campusid;
    }
    public String getPatno() {
        return patno;
    }
    public void setPatno(String patno) {
        this.patno = patno;
    }
    public String getSerialnum() {
        return serialnum;
    }
    public void setSerialnum(String serialnum) {
        this.serialnum = serialnum;
    }
    public String getPatname() {
        return patname;
    }
    public void setPatname(String patname) {
        this.patname = patname;
    }
    public String getInhospno() {
        return inhospno;
    }
    public void setInhospno(String inhospno) {
        this.inhospno = inhospno;
    }
    public Long getAge() {
        return age;
    }
    public void setAge(Long age) {
        this.age = age;
    }
    public Long getSex() {
        return sex;
    }
    public void setSex(Long sex) {
        this.sex = sex;
    }
    public String getTelcode() {
        return telcode;
    }
    public void setTelcode(String telcode) {
        this.telcode = telcode;
    }
    public Date getStarttime() {
        return starttime;
    }
    public void setStarttime(Date starttime) {
        this.starttime = starttime;
    }
    public Date getEndtime() {
        return endtime;
    }
    public void setEndtime(Date endtime) {
        this.endtime = endtime;
    }
    public Date getFudate() {
        return fudate;
    }
    public void setFudate(Date fudate) {
        this.fudate = fudate;
    }
    public String getDeptcode() {
        return deptcode;
    }
    public void setDeptcode(String deptcode) {
        this.deptcode = deptcode;
    }
    public String getDeptname() {
        return deptname;
    }
    public void setDeptname(String deptname) {
        this.deptname = deptname;
    }
    public String getHospitaldistrictcode() {
        return hospitaldistrictcode;
    }
    public void setHospitaldistrictcode(String hospitaldistrictcode) {
        this.hospitaldistrictcode = hospitaldistrictcode;
    }
    public String getHospitaldistrictname() {
        return hospitaldistrictname;
    }
    public void setHospitaldistrictname(String hospitaldistrictname) {
        this.hospitaldistrictname = hospitaldistrictname;
    }
    public String getLeaveldeptcode() {
        return leaveldeptcode;
    }
    public void setLeaveldeptcode(String leaveldeptcode) {
        this.leaveldeptcode = leaveldeptcode;
    }
    public String getLeaveldeptname() {
        return leaveldeptname;
    }
    public void setLeaveldeptname(String leaveldeptname) {
        this.leaveldeptname = leaveldeptname;
    }
    public String getLeavehospitaldistrictcode() {
        return leavehospitaldistrictcode;
    }
    public void setLeavehospitaldistrictcode(String leavehospitaldistrictcode) {
        this.leavehospitaldistrictcode = leavehospitaldistrictcode;
    }
    public String getLeavehospitaldistrictname() {
        return leavehospitaldistrictname;
    }
    public void setLeavehospitaldistrictname(String leavehospitaldistrictname) {
        this.leavehospitaldistrictname = leavehospitaldistrictname;
    }
    public String getLeaveicd10code() {
        return leaveicd10code;
    }
    public void setLeaveicd10code(String leaveicd10code) {
        this.leaveicd10code = leaveicd10code;
    }
    public String getLeavediagname() {
        return leavediagname;
    }
    public void setLeavediagname(String leavediagname) {
        this.leavediagname = leavediagname;
    }
    public String getDiagname() {
        return diagname;
    }
    public void setDiagname(String diagname) {
        this.diagname = diagname;
    }
    public String getNurseId() {
        return nurseId;
    }
    public void setNurseId(String nurseId) {
        this.nurseId = nurseId;
    }
    public String getNurseName() {
        return nurseName;
    }
    public void setNurseName(String nurseName) {
        this.nurseName = nurseName;
    }
    public String getDrcode() {
        return drcode;
    }
    public void setDrcode(String drcode) {
        this.drcode = drcode;
    }
    public String getDrname() {
        return drname;
    }
    public void setDrname(String drname) {
        this.drname = drname;
    }
    public String getManagementDoctor() {
        return managementDoctor;
    }
    public void setManagementDoctor(String managementDoctor) {
        this.managementDoctor = managementDoctor;
    }
    public String getManagementDoctorCode() {
        return managementDoctorCode;
    }
    public void setManagementDoctorCode(String managementDoctorCode) {
        this.managementDoctorCode = managementDoctorCode;
    }
    public String getDeptcheckFlag() {
        return deptcheckFlag;
    }
    public void setDeptcheckFlag(String deptcheckFlag) {
        this.deptcheckFlag = deptcheckFlag;
    }
    public String getWardcheckFlag() {
        return wardcheckFlag;
    }
    public void setWardcheckFlag(String wardcheckFlag) {
        this.wardcheckFlag = wardcheckFlag;
    }
    public String getDiagcheckFlag() {
        return diagcheckFlag;
    }
    public void setDiagcheckFlag(String diagcheckFlag) {
        this.diagcheckFlag = diagcheckFlag;
    }
    public String getScheduleCheckFlag() {
        return scheduleCheckFlag;
    }
    public void setScheduleCheckFlag(String scheduleCheckFlag) {
        this.scheduleCheckFlag = scheduleCheckFlag;
    }
    public String getInDeptcheckFlag() {
        return inDeptcheckFlag;
    }
    public void setInDeptcheckFlag(String inDeptcheckFlag) {
        this.inDeptcheckFlag = inDeptcheckFlag;
    }
    public String getInWardcheckFlag() {
        return inWardcheckFlag;
    }
    public void setInWardcheckFlag(String inWardcheckFlag) {
        this.inWardcheckFlag = inWardcheckFlag;
    }
    public String getInhospstate() {
        return inhospstate;
    }
    public void setInhospstate(String inhospstate) {
        this.inhospstate = inhospstate;
    }
    public String getFuflag() {
        return fuflag;
    }
    public void setFuflag(String fuflag) {
        this.fuflag = fuflag;
    }
    public String getLongTaskReason() {
        return longTaskReason;
    }
    public void setLongTaskReason(String longTaskReason) {
        this.longTaskReason = longTaskReason;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        this.remark = remark;
    }
    public String getMedicalCode() {
        return medicalCode;
    }
    public void setMedicalCode(String medicalCode) {
        this.medicalCode = medicalCode;
    }
    // ==========================================================================
    // ä»¥ä¸‹å­—段【不是 pat_med_inhosp çš„列】,只作为 selectForScan çš„过滤条件/排序开关。
    // æ”¾åœ¨åŒä¸€ä¸ªè¡Œå¯¹è±¡é‡Œï¼Œæ˜¯ä¸ºäº†è®©"查询条件"与"结果行"共用一套属性名 â€”—
    // ä¸ŽåŽŸå®žçŽ°"用 PatMedInhosp å½“查询入参"的写法保持一致,避免两套类来回映射出错。
    // ==========================================================================
    /**
     * å‡ºé™¢/在院维度开关(原 {@code PatMedInhosp.cry}):
     * 0=在院({@code endtime is null})、1=已出院、2=转科、3=预入院。
     * <p>
     * âš ï¸ å®ƒåŒæ—¶å†³å®šæŽ’序:{@code cry=1/2/3} â†’ {@code order by endtime desc};
     * {@code cry=0} â†’ æŒ‰ {@link #bedSort} ä¸‰é€‰ä¸€ã€‚
     */
    private String cry;
    /**
     * åœ¨é™¢æ‚£è€…({@code cry=0})的床位排序方向:"asc" / "desc";其它值走默认排序。
     * <p>
     * åŽŸ {@code PatMedInhospMapper.xml} L382-405 çš„ {@code <choose>} ç”¨å®ƒä¸‰é€‰ä¸€ï¼Œ
     * åºŠä½æŒ‰è‡ªç„¶åºæŽ’(空床位最后 â†’ çº¯æ•°å­—按数值 â†’ å¸¦å­—母按长度+字符串 â†’ inhospid å…œåº•)。
     */
    private String bedSort;
    /** è¿‡æ»¤ï¼šç¦»é™¢ç§‘室编码列表(原 {@code leaveldeptcodes}) */
    private java.util.List<String> leaveldeptcodes;
    /** è¿‡æ»¤ï¼šç¦»é™¢ç—…区编码列表(原 {@code leavehospitaldistrictcodes}) */
    private java.util.List<String> leavehospitaldistrictcodes;
    /** è¿‡æ»¤ï¼šå…¥é™¢ç—…区编码列表(原 {@code hospitaldistrictcodeList}) */
    private java.util.List<String> hospitaldistrictcodeList;
    /** è¿‡æ»¤ï¼šå…¥é™¢ç§‘室编码列表(原 {@code deptcodeList}) */
    private java.util.List<String> deptcodeList;
    /** è¿‡æ»¤ï¼šç¦»é™¢ ICD10 ç¼–码列表(原 {@code icd10codeList}) */
    private java.util.List<String> icd10codeList;
    public String getCry() {
        return cry;
    }
    public void setCry(String cry) {
        this.cry = cry;
    }
    public String getBedSort() {
        return bedSort;
    }
    public void setBedSort(String bedSort) {
        this.bedSort = bedSort;
    }
    public java.util.List<String> getLeaveldeptcodes() {
        return leaveldeptcodes;
    }
    public void setLeaveldeptcodes(java.util.List<String> leaveldeptcodes) {
        this.leaveldeptcodes = leaveldeptcodes;
    }
    public java.util.List<String> getLeavehospitaldistrictcodes() {
        return leavehospitaldistrictcodes;
    }
    public void setLeavehospitaldistrictcodes(java.util.List<String> leavehospitaldistrictcodes) {
        this.leavehospitaldistrictcodes = leavehospitaldistrictcodes;
    }
    public java.util.List<String> getHospitaldistrictcodeList() {
        return hospitaldistrictcodeList;
    }
    public void setHospitaldistrictcodeList(java.util.List<String> hospitaldistrictcodeList) {
        this.hospitaldistrictcodeList = hospitaldistrictcodeList;
    }
    public java.util.List<String> getDeptcodeList() {
        return deptcodeList;
    }
    public void setDeptcodeList(java.util.List<String> deptcodeList) {
        this.deptcodeList = deptcodeList;
    }
    public java.util.List<String> getIcd10codeList() {
        return icd10codeList;
    }
    public void setIcd10codeList(java.util.List<String> icd10codeList) {
        this.icd10codeList = icd10codeList;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitTaskRow.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,444 @@
package com.ruoyi.inhospvisit.domain.row;
import java.io.Serializable;
/**
 * é•¿æœŸéšè®¿ä»»åŠ¡ï¼ˆ{@code service_task})的随访侧只读行。
 *
 * <h3>字段来源(@ b25c3b51)</h3>
 * ç²¾ç¡®ç­‰äºŽ {@code boxedServiceSubtask} ä¸Ž {@code dealOutHospInfoBySchedule} å¯¹
 * {@code serviceTask} çš„ getter è®¿é—®é›†åˆï¼Œå¤–加 {@code addOperationSubTask} ç”¨åˆ°çš„几个:
 * <pre>
 * taskid  type  serviceType  templateid  templatename  libtemplateid  sendDay
 * preachform  preachformDesc  orgid  campusid  updateBy  createBy  longTask  appltype
 * leavehospitaldistrictcode  leavehospitaldistrictcodes  leaveldeptcodes
 * icd10codeList  opLevelCodes(后 4 ä¸ªæ˜¯ä»»åŠ¡ç»„çš„è¿‡æ»¤ç»´åº¦ï¼‰
 * </pre>
 *
 * <h3>⚠️ ä¸ºä»€ä¹ˆè‡ªå·±æ‹¿è€Œä¸å¤ç”¨ VisitLongTaskDto</h3>
 * {@code VisitLongTaskDto} æ˜¯ {@code VisitTaskCreateApi} çš„出参("已发布的任务"),
 * è¯­ä¹‰æ˜¯"任务创建域"的东西;而本行是 {@code service_task} è¡¨çš„读模型,
 * è¿˜è¦æ‰¿è½½ä»»åŠ¡ç»„æ‰«æç”¨çš„è¿‡æ»¤ç»´åº¦ï¼ˆ{@code leavehospitaldistrictcodes} ç­‰ï¼‰ã€‚
 * ä¸¤è€…职责不同,硬合成一个类会让"任务创建模块"将来难以独立演进。
 * ä»£ä»·ï¼šå­—段有部分重叠,由 {@code verify_inhospvisit_equivalence.py} é€å­—段覆盖。
 */
public class VisitTaskRow implements Serializable {
    private static final long serialVersionUID = 1L;
    /** ä»»åŠ¡ID(原 taskid) */
    private Long taskid;
    /** ä»»åŠ¡åç§° */
    private String taskName;
    /** ä»»åŠ¡ç±»åž‹ï¼š"3" = é€šçŸ¥ç±»ï¼ˆå†³å®š visitTime æå‰ N å¤©ï¼‰ */
    private String type;
    /** æœåŠ¡ç±»åž‹ï¼ˆ"2" å‡ºé™¢éšè®¿ / "13" ä¸“病随访 / "17" å®£æ•™å…³æ€€(入院) â€¦ï¼‰ */
    private String serviceType;
    /** é—®å·æ¨¡æ¿ID */
    private Long templateid;
    /** é—®å·æ¨¡æ¿åç§° */
    private String templatename;
    /** è¯­éŸ³æ¨¡æ¿ID(原字段是 String,用 Long.valueOf è½¬æ¢ï¼‰ */
    private String libtemplateid;
    /** æŽ¨é€/发送日偏移天数(缺省按 1 å¤„理) */
    private Long sendDay;
    /** æ‰§è¡Œæ–¹å¼ */
    private String preachform;
    /** æ‰§è¡Œæ–¹å¼æ˜Žç»† JSON(由 addServiceSubtaskPreachform è§£æžï¼‰ */
    private String preachformDesc;
    /** æœºæž„ID */
    private String orgid;
    /** é™¢åŒºID */
    private String campusid;
    /** æ›´æ–°äºº */
    private String updateBy;
    /** åˆ›å»ºäºº */
    private String createBy;
    /** æ˜¯å¦é•¿æœŸä»»åŠ¡ï¼š1 = æ˜¯ */
    private Long longTask;
    /** ç”³è¯·ç±»åž‹ï¼š"5" = ä»»åŠ¡ç»„ */
    private String appltype;
    // ---------- ä»»åŠ¡ç»„ï¼ˆappltype=5)的过滤维度 ----------
    /** ä»»åŠ¡ç»„ï¼šç¦»é™¢ç§‘å®¤ç¼–ç ä¸²ï¼ˆé€—å·åˆ†éš”ï¼‰ */
    private String leavehospitaldistrictcode;
    /** ä»»åŠ¡ç»„ï¼šç¦»é™¢ç§‘å®¤ç¼–ç åˆ—è¡¨ */
    private java.util.List<String> leaveldeptcodes;
    /** ä»»åŠ¡ç»„ï¼šç¦»é™¢ç—…åŒºç¼–ç åˆ—è¡¨ */
    private java.util.List<String> leavehospitaldistrictcodes;
    /** ä»»åŠ¡ç»„ï¼šç–¾ç—… ICD10 ç¼–码列表 */
    private java.util.List<String> icd10codeList;
    /** ä»»åŠ¡ç»„ï¼šæ‰‹æœ¯ç­‰çº§ç¼–ç åˆ—è¡¨ */
    private java.util.List<String> opLevelCodes;
    /** ä»»åŠ¡ç»„ï¼šä¸‹ä¸€ä¸ªä»»åŠ¡ID(随访超时转再次随访用) */
    private Long nexttaskid;
    // ==========================================================================
    // boxed ç»„装需要的另外 13 ä¸ªå­—段(配合已有的 diagType,正好覆盖
    // DtoConversionUtils æ‹·å…¥ä½†æœªè¢«æ˜¾å¼è¦†ç›–的那 18 ä¸ªï¼‰
    // ==========================================================================
    // ä¸ºä»€ä¹ˆè¿™äº›"看起来没用"的字段也要带:原 boxedServiceSubtask ç¬¬ä¸€è¡Œç”¨
    //   DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class)
    // æŠŠ ServiceTask ä¸Ž ServiceSubtask çš„同名字段整批拷过去,其中 18 ä¸ªä¹‹åŽ
    // å†ä¹Ÿæ²¡è¢«æ˜¾å¼ set è¦†ç›– â€”— å®ƒä»¬æ˜¯ã€éšå¼ã€‘进子任务的。少一个就静默丢值。
    // è¯¦è§ VisitSubtaskFactory#applyCarriedOverFieldsFromTask çš„æ³¨é‡Šã€‚
    /** åˆ é™¤æ ‡è®°ï¼ˆåŽŸ serviceTask.delFlag) */
    private String delFlag;
    /** å‡ºé™¢æ—¶é—´åŒºé—´ç»ˆç‚¹ï¼ˆåŽŸ serviceTask.endOutHospTime) */
    private java.util.Date endOutHospTime;
    /** GUID(原 serviceTask.guid) */
    private String guid;
    /** æ˜¯å¦å·²ä¸Šä¼ ï¼ˆåŽŸ serviceTask.isupload) */
    private Long isupload;
    /** è¯­éŸ³æ¨¡æ¿åç§°ï¼ˆåŽŸ serviceTask.libtemplatename) */
    private String libtemplatename;
    /** åˆ†é¡µé¡µç ï¼ˆåŽŸ serviceTask.pageNum) */
    private Integer pageNum;
    /** åˆ†é¡µé¡µé•¿ï¼ˆåŽŸ serviceTask.pageSize) */
    private Integer pageSize;
    /** pid(原 serviceTask.pid æ˜¯ Integer;目标 ServiceSubtask.pid æ˜¯ Long) */
    private Integer pid;
    /** å‘送类型(原 serviceTask.sendType æ˜¯ Long;目标 ServiceSubtask.sendType æ˜¯ String) */
    private Long sendType;
    /** å‡ºé™¢æ—¶é—´åŒºé—´èµ·ç‚¹ï¼ˆåŽŸ serviceTask.startOutHospTime) */
    private java.util.Date startOutHospTime;
    /** ä»»åŠ¡æè¿°ï¼ˆåŽŸ serviceTask.taskDesc) */
    private String taskDesc;
    /** æ–‡æœ¬å‚数(原 serviceTask.textParam) */
    private String textParam;
    /** ä¸Šä¼ æ—¶é—´ï¼ˆåŽŸ serviceTask.uploadTime) */
    private java.util.Date uploadTime;
    // ---------- è¡¥é½çš„字段 ----------
    /** ç–¾ç—…类型(原 serviceTask.diagType;属 DtoConversionUtils æ‹·å…¥çš„ 18 ä¸ªå­—段之一) */
    private String diagType;
    public Long getTaskid() {
        return taskid;
    }
    public void setTaskid(Long taskid) {
        this.taskid = taskid;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public Long getTemplateid() {
        return templateid;
    }
    public void setTemplateid(Long templateid) {
        this.templateid = templateid;
    }
    public String getTemplatename() {
        return templatename;
    }
    public void setTemplatename(String templatename) {
        this.templatename = templatename;
    }
    public String getLibtemplateid() {
        return libtemplateid;
    }
    public void setLibtemplateid(String libtemplateid) {
        this.libtemplateid = libtemplateid;
    }
    public Long getSendDay() {
        return sendDay;
    }
    public void setSendDay(Long sendDay) {
        this.sendDay = sendDay;
    }
    public String getPreachform() {
        return preachform;
    }
    public void setPreachform(String preachform) {
        this.preachform = preachform;
    }
    public String getPreachformDesc() {
        return preachformDesc;
    }
    public void setPreachformDesc(String preachformDesc) {
        this.preachformDesc = preachformDesc;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getCampusid() {
        return campusid;
    }
    public void setCampusid(String campusid) {
        this.campusid = campusid;
    }
    public String getUpdateBy() {
        return updateBy;
    }
    public void setUpdateBy(String updateBy) {
        this.updateBy = updateBy;
    }
    public String getCreateBy() {
        return createBy;
    }
    public void setCreateBy(String createBy) {
        this.createBy = createBy;
    }
    public Long getLongTask() {
        return longTask;
    }
    public void setLongTask(Long longTask) {
        this.longTask = longTask;
    }
    public String getAppltype() {
        return appltype;
    }
    public void setAppltype(String appltype) {
        this.appltype = appltype;
    }
    public String getLeavehospitaldistrictcode() {
        return leavehospitaldistrictcode;
    }
    public void setLeavehospitaldistrictcode(String leavehospitaldistrictcode) {
        this.leavehospitaldistrictcode = leavehospitaldistrictcode;
    }
    public java.util.List<String> getLeaveldeptcodes() {
        return leaveldeptcodes;
    }
    public void setLeaveldeptcodes(java.util.List<String> leaveldeptcodes) {
        this.leaveldeptcodes = leaveldeptcodes;
    }
    public java.util.List<String> getLeavehospitaldistrictcodes() {
        return leavehospitaldistrictcodes;
    }
    public void setLeavehospitaldistrictcodes(java.util.List<String> leavehospitaldistrictcodes) {
        this.leavehospitaldistrictcodes = leavehospitaldistrictcodes;
    }
    public java.util.List<String> getIcd10codeList() {
        return icd10codeList;
    }
    public void setIcd10codeList(java.util.List<String> icd10codeList) {
        this.icd10codeList = icd10codeList;
    }
    public java.util.List<String> getOpLevelCodes() {
        return opLevelCodes;
    }
    public void setOpLevelCodes(java.util.List<String> opLevelCodes) {
        this.opLevelCodes = opLevelCodes;
    }
    public Long getNexttaskid() {
        return nexttaskid;
    }
    public void setNexttaskid(Long nexttaskid) {
        this.nexttaskid = nexttaskid;
    }
    // ---------- ä¸Šè¿° 13 ä¸ªæ‰¿è½½å­—段的访问器 ----------
    public String getDelFlag() {
        return delFlag;
    }
    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }
    public java.util.Date getEndOutHospTime() {
        return endOutHospTime;
    }
    public void setEndOutHospTime(java.util.Date endOutHospTime) {
        this.endOutHospTime = endOutHospTime;
    }
    public String getGuid() {
        return guid;
    }
    public void setGuid(String guid) {
        this.guid = guid;
    }
    public Long getIsupload() {
        return isupload;
    }
    public void setIsupload(Long isupload) {
        this.isupload = isupload;
    }
    public String getLibtemplatename() {
        return libtemplatename;
    }
    public void setLibtemplatename(String libtemplatename) {
        this.libtemplatename = libtemplatename;
    }
    public Integer getPageNum() {
        return pageNum;
    }
    public void setPageNum(Integer pageNum) {
        this.pageNum = pageNum;
    }
    public Integer getPageSize() {
        return pageSize;
    }
    public void setPageSize(Integer pageSize) {
        this.pageSize = pageSize;
    }
    public Integer getPid() {
        return pid;
    }
    public void setPid(Integer pid) {
        this.pid = pid;
    }
    public Long getSendType() {
        return sendType;
    }
    public void setSendType(Long sendType) {
        this.sendType = sendType;
    }
    public java.util.Date getStartOutHospTime() {
        return startOutHospTime;
    }
    public void setStartOutHospTime(java.util.Date startOutHospTime) {
        this.startOutHospTime = startOutHospTime;
    }
    public String getTaskDesc() {
        return taskDesc;
    }
    public void setTaskDesc(String taskDesc) {
        this.taskDesc = taskDesc;
    }
    public String getTextParam() {
        return textParam;
    }
    public void setTextParam(String textParam) {
        this.textParam = textParam;
    }
    public java.util.Date getUploadTime() {
        return uploadTime;
    }
    public void setUploadTime(java.util.Date uploadTime) {
        this.uploadTime = uploadTime;
    }
    // ---------- è¡¥é½å­—段的访问器 ----------
    public String getDiagType() {
        return diagType;
    }
    public void setDiagType(String diagType) {
        this.diagType = diagType;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitTaskdeptRow.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,170 @@
package com.ruoyi.inhospvisit.domain.row;
import java.util.Date;
/**
 * é•¿æœŸéšè®¿ä»»åŠ¡Â·ç§‘å®¤/病区维度({@code service_taskdept})的随访侧只读行。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * å¯¹åº” {@code ServiceTaskdept}(10 ä¸ªè‡ªæœ‰å­—段 + {@code createTime/updateTime/orgid}),
 * åˆ—清单逐条对齐 {@code ServiceTaskdeptMapper.xml} çš„
 * {@code selectServiceTaskdeptVo}(L23-38)与 {@code ServiceTaskdeptResult}(L7-21)。
 *
 * <h3>本行服务哪些随访逻辑</h3>
 * <ul>
 *   <li>{@code addDeptSubTaskForPatient}(L1854-1887):{@code deptType="1"} + {@code serviceType="2"}</li>
 *   <li>{@code addWardSubTaskForPatient}(L1894-1935):{@code deptType="2"} + {@code serviceType="2"}</li>
 *   <li>{@code dealOutHospInfo} çš„入院宣教:{@code deptType="1"/"2"} + {@code serviceType="17"}
 *       â€”— é‚£æ®µå·²å½’ {@code ruoyi-publicity},走 {@code PublicityDataPort}</li>
 * </ul>
 *
 * <h3>⚠️ ä¸Ž smartor çš„ {@code ServiceTaskdeptMapper} æ˜¯ä¸¤å¥—等价的 SQL</h3>
 * è¿™æ˜¯ä½ å·²ç¡®è®¤çš„"新模块自携 SQL"方案。纪律同 {@code VisitInhospRow}:
 * æ”¹ä»»ä¸€è¾¹å¿…须同步另一边,并重跑
 * {@code verify_inhospvisit_mapper.py} ä¸Ž {@code verify_inhospvisit_sql_expansion.py}。
 */
public class VisitTaskdeptRow {
    /** ä¸»é”® */
    private Long id;
    /** ä»»åŠ¡ID(原 {@code taskId}) */
    private Long taskId;
    /** ä»»åŠ¡åç§° */
    private String taskName;
    /** æ˜¯å¦é•¿æœŸä»»åŠ¡ï¼š1 = æ˜¯ */
    private Long longtask;
    /** æœåŠ¡ç±»åž‹ï¼ˆ"2" å‡ºé™¢éšè®¿ / "17" å®£æ•™å…³æ€€(入院) â€¦ï¼‰ */
    private String serviceType;
    /** ç»´åº¦ç±»åž‹ï¼š"1" = ç§‘室,"2" = ç—…区 */
    private String deptType;
    /** ç§‘室ID */
    private Long deptId;
    /** ç§‘室/病区编码 */
    private String deptCode;
    /** ç§‘室/病区名称 */
    private String deptName;
    /** åˆ é™¤æ ‡è®° */
    private String delFlag;
    /** æœºæž„ID */
    private String orgid;
    /** åˆ›å»ºæ—¶é—´ */
    private Date createTime;
    /** æ›´æ–°æ—¶é—´ */
    private Date updateTime;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Long getTaskId() {
        return taskId;
    }
    public void setTaskId(Long taskId) {
        this.taskId = taskId;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public Long getLongtask() {
        return longtask;
    }
    public void setLongtask(Long longtask) {
        this.longtask = longtask;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public String getDeptType() {
        return deptType;
    }
    public void setDeptType(String deptType) {
        this.deptType = deptType;
    }
    public Long getDeptId() {
        return deptId;
    }
    public void setDeptId(Long deptId) {
        this.deptId = deptId;
    }
    public String getDeptCode() {
        return deptCode;
    }
    public void setDeptCode(String deptCode) {
        this.deptCode = deptCode;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public String getDelFlag() {
        return delFlag;
    }
    public void setDelFlag(String delFlag) {
        this.delFlag = delFlag;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/domain/row/VisitTaskdiagRow.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,232 @@
package com.ruoyi.inhospvisit.domain.row;
import java.util.Date;
/**
 * é•¿æœŸéšè®¿ä»»åŠ¡Â·ç–¾ç—…ç»´åº¦ï¼ˆ{@code service_taskdiag})的随访侧只读行。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * å¯¹åº” {@code ServiceTaskdiag}(17 ä¸ªå­—段),列清单逐条对齐
 * {@code ServiceTaskdiagMapper.xml} çš„ {@code selectServiceTaskdiagVo}(L29-50)
 * ä¸Ž {@code ServiceTaskdiagResult}(L7-27)。
 *
 * <h3>本行服务哪些随访逻辑</h3>
 * {@code addIcd10SubTaskForPatient}(L1942-2001)用
 * {@code selectServiceTaskdiagListByIcdName} æŸ¥é•¿æœŸä»»åŠ¡ï¼Œç„¶åŽè¦æ±‚
 * <b>{@code deptCode} æˆ– {@code wardCode} ä¸Žæ‚£è€…的离院科室/病区匹配</b>才建子任务
 * ï¼ˆåŽŸ L1975-1983)—— æ‰€ä»¥ {@code deptCode/wardCode} æ˜¯è¿™ä¸€ç»´åº¦æœ€å…³é”®çš„字段。
 *
 * <h3>⚠️ ä¸Ž smartor çš„ {@code ServiceTaskdiagMapper} æ˜¯ä¸¤å¥—等价的 SQL</h3>
 * çºªå¾‹åŒ {@code VisitInhospRow}。
 */
public class VisitTaskdiagRow {
    /** ä¸»é”® */
    private Long id;
    /** ä»»åŠ¡ID(原 {@code taskId}) */
    private Long taskId;
    /** æ˜¯å¦é•¿æœŸä»»åŠ¡ï¼š1 = æ˜¯ */
    private Long longtask;
    /** ä»»åŠ¡åç§° */
    private String taskName;
    /** æœåŠ¡ç±»åž‹ */
    private String serviceType;
    /** ICD10 ä¸»é”® */
    private Long icd10id;
    /** ICD10 ç¼–码 */
    private String icd10code;
    /** ICD10 åç§°ï¼ˆè¯Šæ–­åï¼‰ */
    private String icd10name;
    /** åˆ é™¤æ ‡è®°ï¼ˆæ³¨æ„ï¼šåŽŸå®žä½“é‡Œæ˜¯ {@code Integer} ç±»åž‹ï¼‰ */
    private Integer delFlag;
    /** æœºæž„ID */
    private String orgid;
    /** GUID */
    private String guid;
    /** çˆ¶ GUID */
    private String pguid;
    /** pid */
    private Long pid;
    /** ç§‘室编码(关键:必须与患者离院科室匹配) */
    private String deptCode;
    /** ç§‘室名称 */
    private String deptName;
    /** ç—…区编码(关键:必须与患者离院病区匹配) */
    private String wardCode;
    /** ç—…区名称 */
    private String wardName;
    /** åˆ›å»ºæ—¶é—´ */
    private Date createTime;
    /** æ›´æ–°æ—¶é—´ */
    private Date updateTime;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public Long getTaskId() {
        return taskId;
    }
    public void setTaskId(Long taskId) {
        this.taskId = taskId;
    }
    public Long getLongtask() {
        return longtask;
    }
    public void setLongtask(Long longtask) {
        this.longtask = longtask;
    }
    public String getTaskName() {
        return taskName;
    }
    public void setTaskName(String taskName) {
        this.taskName = taskName;
    }
    public String getServiceType() {
        return serviceType;
    }
    public void setServiceType(String serviceType) {
        this.serviceType = serviceType;
    }
    public Long getIcd10id() {
        return icd10id;
    }
    public void setIcd10id(Long icd10id) {
        this.icd10id = icd10id;
    }
    public String getIcd10code() {
        return icd10code;
    }
    public void setIcd10code(String icd10code) {
        this.icd10code = icd10code;
    }
    public String getIcd10name() {
        return icd10name;
    }
    public void setIcd10name(String icd10name) {
        this.icd10name = icd10name;
    }
    public Integer getDelFlag() {
        return delFlag;
    }
    public void setDelFlag(Integer delFlag) {
        this.delFlag = delFlag;
    }
    public String getOrgid() {
        return orgid;
    }
    public void setOrgid(String orgid) {
        this.orgid = orgid;
    }
    public String getGuid() {
        return guid;
    }
    public void setGuid(String guid) {
        this.guid = guid;
    }
    public String getPguid() {
        return pguid;
    }
    public void setPguid(String pguid) {
        this.pguid = pguid;
    }
    public Long getPid() {
        return pid;
    }
    public void setPid(Long pid) {
        this.pid = pid;
    }
    public String getDeptCode() {
        return deptCode;
    }
    public void setDeptCode(String deptCode) {
        this.deptCode = deptCode;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
    public String getWardCode() {
        return wardCode;
    }
    public void setWardCode(String wardCode) {
        this.wardCode = wardCode;
    }
    public String getWardName() {
        return wardName;
    }
    public void setWardName(String wardName) {
        this.wardName = wardName;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/PatMedInhospMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,123 @@
package com.ruoyi.inhospvisit.mapper;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.ruoyi.inhospvisit.domain.PatMedInhosp;
import com.ruoyi.inhospvisit.domain.PatMedInhospVO;
import com.ruoyi.common.core.service.dto.PatMedReq;
import com.ruoyi.common.core.service.dto.PatMedRes;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * æ‚£è€…住院记录Mapper接口
 *
 * @author smartor
 * @date 2023-03-04
 */
@Mapper
public interface PatMedInhospMapper {
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录
     *
     * @param inhospid æ‚£è€…住院记录主键
     * @return æ‚£è€…住院记录
     */
    public PatMedInhosp selectPatMedInhospByInhospid(Long inhospid);
    public List<PatMedInhosp> selectPatMedInhosp(PatMedInhosp patMedInhosp);
    /**
     * æŒ‰æ‚£è€…编号 + å‡ºé™¢æ—¥æœŸæŸ¥è¯¢å‡ºé™¢è®°å½•(出院时间只比较到天,不比较时分秒)
     *
     * @param patno    æ‚£è€…编号
     * @param dayStart æŸ¥è¯¢æ—¥æœŸï¼ˆå½“天 00:00:00)
     * @param dayEnd   æŸ¥è¯¢æ—¥æœŸçš„æ¬¡æ—¥ 00:00:00
     * @return å‡ºé™¢è®°å½•集合,按 inhospid å€’序
     */
    public List<PatMedInhosp> selectOutInhospByPatnoAndOutDate(@Param("patno") String patno,
                                                               @Param("dayStart") Date dayStart,
                                                               @Param("dayEnd") Date dayEnd);
    public String getTagnameBypatid(Long patid);
    /**
     * æ‰¹é‡æŸ¥è¯¢æ‚£è€…标签(GROUP_CONCAT æ‹¼æŽ¥åŽçš„æ ‡ç­¾ä¸²ï¼‰
     *
     * @param patids æ‚£è€…档案id集合,调用方须保证非空且已去重
     * @return æ¯è¡Œå« patid、tagname ä¸¤ä¸ª key
     */
    public List<Map<String, Object>> getTagnameByPatids(@Param("patids") List<Long> patids);
    /**
     * æŸ¥è¯¢æ‚£è€…住院记录列表
     *
     * @param patMedInhosp æ‚£è€…住院记录
     * @return æ‚£è€…住院记录集合
     */
    public List<PatMedInhosp> selectPatMedInhospList(PatMedInhosp patMedInhosp);
    public List<PatMedInhosp> selectPatMedInhospListBySerialnum(PatMedInhosp patMedInhosp);
    public List<PatMedInhosp> selectPatMedInhospListByCondition(PatMedInhospVO patMedInhospVO);
    /**
     * æ–°å¢žæ‚£è€…住院记录
     *
     * @param patMedInhosp æ‚£è€…住院记录
     * @return ç»“æžœ
     */
    public int insertPatMedInhosp(PatMedInhosp patMedInhosp);
    public int insertPatMedInhospBatch(@Param("list") List<PatMedInhosp> list);
    /**
     * ä¿®æ”¹æ‚£è€…住院记录
     *
     * @param patMedInhosp æ‚£è€…住院记录
     * @return ç»“æžœ
     */
    public int updatePatMedInhosp(PatMedInhosp patMedInhosp);
    /**
     * åˆ é™¤æ‚£è€…住院记录
     *
     * @param inhospid æ‚£è€…住院记录主键
     * @return ç»“æžœ
     */
    public int deletePatMedInhospByInhospid(Long inhospid);
    /**
     * åˆ é™¤æ‚£è€…住院记录
     *
     * @param inhospno æ‚£è€…住院编码
     * @return ç»“æžœ
     */
    public int deletePatMedInhospByInhospno(String inhospno);
    /**
     * æ‰¹é‡åˆ é™¤æ‚£è€…住院记录
     *
     * @param inhospids éœ€è¦åˆ é™¤çš„æ•°æ®ä¸»é”®é›†åˆ
     * @return ç»“æžœ
     */
    public int deletePatMedInhospByInhospids(Long[] inhospids);
    /**
     * å‡ºé™¢ã€å…¥é™¢äººæ•°
     *
     * @param patMedReq
     * @return
     */
    public PatMedRes selectPatMedInhospCount(PatMedReq patMedReq);
    /**
     * æŸ¥è¯¢ä¸‰ä¸ªç»´åº¦ä»»æ„ä¸€ä¸ªæœªå¤„理的出院患者
     *
     * @return
     */
    public List<PatMedInhosp> selectNeedProcessList();
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitArchiveMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
package com.ruoyi.inhospvisit.mapper;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
/**
 * æ‚£è€…档案只读 Mapper({@code pat_archive})—— å‡ºå…¥é™¢éšè®¿æ¨¡å—自带。
 *
 * <h3>⚠️ ä¸Ž smartor çš„ {@code PatArchiveMapper} æ˜¯ä¸¤å¥—查询</h3>
 * æœ¬æ¨¡å—只依赖 ruoyi-common(不依赖 smartor,否则循环依赖),所以自己查。
 * <b>纪律</b>:本接口的 SQL åªéœ€è¦†ç›–"随访组装真正读到的列",
 * å¹¶ç”± {@code sql/_probe/verify_inhospvisit_mapper.py} ä¸Ž
 * {@code verify_inhospvisit_sql_expansion.py} æŠŠå…³ï¼›æ”¹ä»»ä¸€è¾¹å¿…须同步另一边。
 *
 * <h3>对应的原实现(@ b25c3b51)</h3>
 * {@code PatMedInhospServiceImpl} é‡Œ 10 å¤„
 * {@code patArchiveMapper.selectPatArchiveByPatid(patMedInhosp.getPatid())}
 * ï¼ˆL338/382/436/536/573/948/1447/1546/1614/1694)。
 */
@Mapper
public interface VisitArchiveMapper {
    /**
     * æŒ‰æ‚£è€…档案ID查(原 {@code PatArchiveMapper#selectPatArchiveByPatid})。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°æ˜¯ {@code select ... from pat_archive where id = #{patid}}(单表、无 del_flag æ¡ä»¶ï¼‰ï¼Œ
     * è¿™é‡Œ<b>逐字照搬</b>,不加过滤条件 â€”— åŠ äº†å°±ä¼šæ”¹å˜"查得到/查不到"的判断,
     * è¿›è€Œæ”¹å˜ {@code "患者基本信息为空"} åˆ†æ”¯çš„走向。
     *
     * @param patid æ‚£è€…档案ID
     * @return æ¡£æ¡ˆè¡Œï¼›ä¸å­˜åœ¨è¿”回 {@code null}
     */
    VisitArchiveRow selectByPatid(@Param("patid") Long patid);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitInhospMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,104 @@
package com.ruoyi.inhospvisit.mapper;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * å‡ºå…¥é™¢è®°å½•({@code pat_med_inhosp})随访侧 Mapper â€”— å‡ºå…¥é™¢éšè®¿æ¨¡å—自带。
 *
 * <h3>⚠️ ä¸Ž smartor çš„ {@code PatMedInhospMapper} æ˜¯ä¸¤å¥—查询(本模块唯一真正的 SQL é‡å¤ï¼‰</h3>
 * åŒä¸€å¼ è¡¨ã€ä¸¤å¥— SQL,理由见 {@link VisitInhospRow} çš„类注释。纪律:
 * <ul>
 *   <li>两边的 SQL å¿…é¡»<b>逐条等价</b>;</li>
 *   <li>由 {@code sql/_probe/verify_inhospvisit_sql_expansion.py} æŠŠä¸¤è¾¹çš„ WHERE/GROUP BY/ORDER BY
 *       åŠ¨æ€å±•å¼€åŽé€æ¡æ¯”å¯¹ï¼ˆAND é¡ºåºæ— å…³ã€{@code #{}}→{@code ?}、列清单折叠);</li>
 *   <li>由 {@code sql/_probe/verify_inhospvisit_mapper.py} æ ¡éªŒæŽ¥å£æ–¹æ³•↔XML statement ä¸€ä¸€å¯¹åº”ï¼›</li>
 *   <li><b>改任一边必须同步改另一边</b>,并重跑这两个探针。</li>
 * </ul>
 *
 * <h3>方法对应的原实现(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本方法</th><th>原调用点</th></tr>
 *   <tr><td>{@link #selectForScan}</td><td>{@code PatMedInhospServiceImpl} L329/458/527/1195/1328/1438/1529/1598
 *       ï¼ˆéƒ½æ˜¯ {@code selectPatMedInhospList})</td></tr>
 *   <tr><td>{@link #selectByInhospid}</td><td>L356/399/480/632/1409/1480/1505/1572/1632/1867/1915/1955/1968
 *       ï¼ˆéƒ½æ˜¯ {@code selectPatMedInhospByInhospid})</td></tr>
 *   <tr><td>{@link #updateFlags}</td><td>{@code updatePatMedInhosp}(多处,只回写标志位与 reason)</td></tr>
 * </table>
 */
@Mapper
public interface VisitInhospMapper {
    /**
     * éšè®¿æ‰«æ/按标志位过滤(原 {@code PatMedInhospMapper#selectPatMedInhospList})。
     * <p>
     * âš ï¸ <b>必须逐字等价地保留原 SQL çš„三处关键语义</b>:
     * <ol>
     *   <li>JOIN {@code pat_archive} ä¸”要求 {@code a.del_flag = 0}、{@code b.del_flag = 0}、
     *       {@code a.idcardno IS NOT NULL} â€”— è¿™ä¸‰ä¸ªæ¡ä»¶å†³å®š"谁会被扫到";</li>
     *   <li>{@code GROUP BY b.inhospid, a.age, a.age_unit, a.telcode, a.id, a.sex} â€”— åŽ»é‡è¯­ä¹‰ï¼›</li>
     *   <li>排序:{@code cry=1/2/3} æ—¶ {@code order by b.endtime desc};
     *       {@code cry=0} æ—¶æŒ‰ {@code bedSort} ä¸‰é€‰ä¸€ï¼ˆå«åºŠä½è‡ªç„¶åºçš„复杂表达式),
     *       é»˜è®¤ {@code order by b.starttime desc, b.bed_no asc}。
     *       <b>顺序会影响处理次序与最终结果</b>(尤其再入院停机逻辑),不能简化。</li>
     * </ol>
     *
     * @param query è¿‡æ»¤æ¡ä»¶ï¼ˆéšè®¿ä¾§åªç”¨åˆ° flag / inhospstate / fuflag / orgid / cry / bedSort / patid ç­‰ï¼‰
     * @return åŒ¹é…è¡Œ
     */
    List<VisitInhospRow> selectForScan(VisitInhospRow query);
    /**
     * æŒ‰ä¸»é”®æŸ¥ï¼ˆåŽŸ {@code PatMedInhospMapper#selectPatMedInhospByInhospid})。
     *
     * @param inhospid å‡ºå…¥é™¢è®°å½•ID
     * @return è®°å½•行;不存在返回 {@code null}
     */
    VisitInhospRow selectByInhospid(@Param("inhospid") Long inhospid);
    /**
     * åªå›žå†™éšè®¿æ ‡å¿—位与 {@code long_task_reason}(原 {@code updatePatMedInhosp} çš„随访用途子集)。
     * <p>
     * âš ï¸ <b>刻意不做成"全字段动态更新"</b>:原 {@code updatePatMedInhosp} æœ‰è¿‘百个
     * {@code <if test="xxx != null">},调用方传什么就更新什么。随访侧只写这几列,
     * æ‰€ä»¥è¿™é‡Œæ˜¾å¼åˆ—出,避免"传了个半满的实体导致某些列被意外写空/漏写"。
     * <p>
     * ä¼ å…¥çš„ {@code reason} ä¸º null æ—¶<b>不改动</b> {@code long_task_reason}(与原 {@code <if>} ä¸€è‡´ï¼‰ã€‚
     *
     * @param inhospid å‡ºå…¥é™¢è®°å½•ID
     * @param flagColumn æ ‡å¿—位列名,只允许
     *        {@code deptcheck_flag} / {@code wardcheck_flag} / {@code diagcheck_flag} /
     *        {@code medcheck_flag} / {@code in_deptcheck_flag} / {@code in_wardcheck_flag} /
     *        {@code schedulecheck_flag}
     *        ï¼ˆç™½åå•在 Service å±‚校验,SQL é‡Œç”¨ {@code <choose>} å±•开成常量列名,不做字符串拼接)
     * @param flagValue  æ ‡å¿—位值("0" / "1" / "2" â€¦ï¼‰
     * @param reason     {@code long_task_reason} çš„æ–°å€¼ï¼›null = ä¸æ”¹åЍ
     * @return å½±å“è¡Œæ•°
     */
    int updateFlags(@Param("inhospid") Long inhospid,
                    @Param("flagColumn") String flagColumn,
                    @Param("flagValue") String flagValue,
                    @Param("reason") String reason);
    /**
     * åªå›žå†™ {@code remark}(原 {@code updateCheckFlag} çš„附带动作,L1680-1683)。
     * <p>
     * åŽŸå®žçŽ°æ˜¯åœ¨åŒä¸€ä¸ª"整实体动态更新"里顺手写 remark çš„:
     * <pre>
     * if (StringUtils.isNotEmpty(remark)) {
     *     updateObj.setRemark(remark + " (type=" + type + ")");
     * }
     * patMedInhospMapper.updatePatMedInhosp(updateObj);
     * </pre>
     * å› ä¸º {@code updatePatMedInhosp} æ˜¯"每个字段各自 {@code <if test="xxx != null">}",
     * æ‰€ä»¥æ‹†æˆä¸¤æ¬¡ update(flag ä¸€æ¬¡ã€remark ä¸€æ¬¡ï¼‰ä¸ŽåŽŸæ¥çš„"一次整实体更新"在结果上等价。
     *
     * @param inhospid å‡ºå…¥é™¢è®°å½•ID
     * @param remark   æ–°çš„备注(覆盖式)
     * @return å½±å“è¡Œæ•°
     */
    int updateRemark(@Param("inhospid") Long inhospid, @Param("remark") String remark);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitPreachformMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package com.ruoyi.inhospvisit.mapper;
import com.ruoyi.common.core.service.dto.VisitPreachformRow;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * éšè®¿å­ä»»åŠ¡ã€Œæ‰§è¡Œæ–¹å¼æ˜Žç»†ã€ï¼ˆ{@code service_subtask_preachform})Mapper â€”— å‡ºå…¥é™¢éšè®¿æ¨¡å—自带。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本方法</th><th>原方法</th><th>原 XML</th></tr>
 *   <tr><td>{@link #selectList}</td><td>{@code selectServiceSubtaskPreachformList}</td>
 *       <td>L44-73({@code del_flag = 0} + 8 ä¸ªå¯é€‰æ¡ä»¶ï¼‰</td></tr>
 *   <tr><td>{@link #insert}</td><td>{@code insertServiceSubtaskPreachform}</td>
 *       <td>L81-151({@code useGeneratedKeys},15 ä¸ªå¯é€‰åˆ—)</td></tr>
 * </table>
 *
 * <h3>⚠️ ä¸Ž smartor çš„ Mapper æ˜¯ä¸¤å¥—等价的 SQL</h3>
 * åŒ {@code VisitInhospMapper} çš„纪律:改任一边必须同步另一边,并重跑
 * {@code verify_inhospvisit_mapper.py} ä¸Ž {@code verify_inhospvisit_sql_expansion.py}。
 */
@Mapper
public interface VisitPreachformMapper {
    /**
     * æŒ‰æ¡ä»¶æŸ¥ï¼ˆåŽŸ {@code selectServiceSubtaskPreachformList})。
     * <p>
     * åŽŸ XML çš„固定条件是 {@code del_flag = 0};其余 8 ä¸ªå­—段为可选条件:
     * {@code subid / taskid / current_send_time / preachform / sendstate / orgid / sort / compensate_time}。
     * <b>全部照抄</b>——虽然调用点只用到 {@code subid + taskid + preachform},
     * ä½†å®Œæ•´ç…§æŠ„后"等价"就是可机械验证的事实(这是第 6d æ­¥è¿”工学到的)。
     */
    List<VisitPreachformRow> selectList(VisitPreachformRow query);
    /**
     * æ–°å¢žï¼ˆåŽŸ {@code insertServiceSubtaskPreachform},自增主键回填到 {@code id})。
     */
    int insert(VisitPreachformRow row);
    // ==========================================================================
    // ä»¥ä¸‹ 7 ä¸ªæ–¹æ³•为「跨模块 SQL æ”¶å£ã€è¡¥é½ â€”— åŽŸ smartor çš„
    // ServiceSubtaskPreachformMapper å…± 9 ä¸ªæ–¹æ³•,本模块原先只有 2 ä¸ªã€‚
    // é€æ–¹æ³•与原 XML å¯¹åº”,条件/列/顺序照抄,不做精简。
    // ==========================================================================
    /**
     * æŒ‰ä¸»é”®æŸ¥ï¼ˆåŽŸ {@code selectServiceSubtaskPreachformById})。
     */
    VisitPreachformRow selectById(Long id);
    /**
     * æŒ‰ {@code taskid + orgid} æŸ¥ï¼ˆåŽŸ {@code selectBySubtask})。
     * <p>
     * âš ï¸ åŽŸæ–¹æ³•åå®¹æ˜“è¯¯è§£ï¼šå®ƒ<b>不按 subtask ä¸»é”®æŸ¥</b>,而是以
     * {@code del_flag = 0} åŠ å¯é€‰çš„ {@code taskid}、{@code orgid} ä¸ºæ¡ä»¶ã€‚
     * å‚数类型原为 {@code ServiceSubtask},实际只用到这两个字段。
     */
    List<VisitPreachformRow> selectByTaskidAndOrgid(@Param("taskid") Long taskid,
                                                    @Param("orgid") String orgid);
    /**
     * æŒ‰ä¸»é”®æ›´æ–°ï¼ˆåŽŸ {@code updateServiceSubtaskPreachform})。
     * <p>原 XML çš„ WHERE æ˜¯ {@code where id = #{id}};SET å„列均为 {@code <if test="xxx != null">}。
     */
    int updateById(VisitPreachformRow row);
    /**
     * æŒ‰æ¡ä»¶æ›´æ–°ï¼ˆåŽŸ {@code updateSSPByCondition})。
     * <p>
     * SET å­å¥ä¸Ž {@link #updateById} <b>完全相同</b>,区别只在 WHERE:
     * è¿™é‡ŒæŒ‰ {@code subid / taskid / preachform / remark / orgid} é€é¡¹å¯é€‰ï¼Œ
     * ä¸”<b>没有 id æ¡ä»¶</b>。两处刻意分开保留,不合并 â€”— åˆå¹¶ä¼šæ”¹å˜"按什么定位行"的语义。
     */
    int updateByCondition(VisitPreachformRow row);
    /**
     * æŒ‰ä¸»é”®è½¯åˆ ï¼ˆåŽŸ {@code deleteServiceSubtaskPreachformById})。
     * <p>⚠️ åŽŸæ ‡ç­¾æ˜¯ {@code <update>} ä¸æ˜¯ {@code delete}:实际执行 {@code set del_flag = 1}。
     */
    int softDeleteById(Long id);
    /**
     * æŒ‰ä¸»é”®é›†åˆè½¯åˆ ï¼ˆåŽŸ {@code deleteServiceSubtaskPreachformByIds})。
     * <p>⚠️ åŒæ ·æ˜¯ {@code <update>},且走 {@code array} é›†åˆï¼š{@code where id in (...)}。
     */
    int softDeleteByIds(Long[] ids);
    /**
     * æŒ‰ {@code subid} è½¯åˆ ï¼ˆåŽŸ {@code deleteServiceSubtaskPreachformBySubid})。
     * <p>⚠️ åŽŸæ ‡ç­¾æ˜¯ {@code <delete>} ä½†è¯­å¥æ˜¯ {@code update ... set del_flag = 1}(原样保留)。
     */
    int softDeleteBySubid(Long subid);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitTaskdeptMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,40 @@
package com.ruoyi.inhospvisit.mapper;
import com.ruoyi.inhospvisit.domain.row.VisitTaskdeptRow;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * é•¿æœŸéšè®¿ä»»åŠ¡Â·ç§‘å®¤/病区维度({@code service_taskdept})Mapper â€”— å‡ºå…¥é™¢éšè®¿æ¨¡å—自带。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * åªæ¬éšè®¿ä¾§ç”¨åˆ°çš„那一个查询:{@code ServiceTaskdeptMapper#selectServiceTaskdeptList}
 * ï¼ˆåŽŸ XML L40-72)。其余 CRUD å±žä»»åŠ¡é…ç½®ç«¯ï¼Œ<b>不搬</b>。
 *
 * <h3>⚠️ åŽŸ XML çš„一处 bug å¿…须原样保留</h3>
 * {@code dept_name} é‚£ä¸ªæ¡ä»¶å†™çš„æ˜¯ï¼š
 * <pre>
 * &lt;if test="deptName != null  and <b>deptCode</b> != ''"&gt;   â† åˆ¤çš„æ˜¯ deptCode,不是 deptName
 *     and dept_name = #{deptName}
 * &lt;/if&gt;
 * </pre>
 * å³"是否拼 dept_name æ¡ä»¶"由 {@code deptCode} æ˜¯å¦ä¸ºç©ºå†³å®šã€‚
 * éšè®¿è°ƒç”¨ç‚¹<b>从不设 deptName</b>(只设 deptCode),所以这个 bug åœ¨éšè®¿é“¾ä¸Šä¸å¯è¾¾ â€”—
 * ä½†æŒ‰ç¬¬ 6d æ­¥å­¦åˆ°çš„原则(不靠"不可达"来删条件),这里<b>照抄原样</b>。
 *
 * <h3>⚠️ ä¸Ž smartor çš„ Mapper æ˜¯ä¸¤å¥—等价 SQL</h3>
 * çºªå¾‹åŒ {@code VisitInhospMapper}:改任一边必须同步另一边并重跑两个探针。
 */
@Mapper
public interface VisitTaskdeptMapper {
    /**
     * æŒ‰ç»´åº¦æŸ¥é•¿æœŸä»»åŠ¡ï¼ˆåŽŸ {@code selectServiceTaskdeptList})。
     * <p>
     * å›ºå®šæ¡ä»¶ {@code del_flag = 0};可选条件(<b>顺序与内容逐条照抄</b>):
     * {@code task_id / task_name(=) / longtask / service_type / dept_type /
     * dept_id / dept_code / dept_name(bug è§ç±»æ³¨é‡Š) / orgid}。
     */
    List<VisitTaskdeptRow> selectList(VisitTaskdeptRow query);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/mapper/VisitTaskdiagMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package com.ruoyi.inhospvisit.mapper;
import com.ruoyi.inhospvisit.domain.row.VisitTaskdiagRow;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * é•¿æœŸéšè®¿ä»»åŠ¡Â·ç–¾ç—…ç»´åº¦ï¼ˆ{@code service_taskdiag})Mapper â€”— å‡ºå…¥é™¢éšè®¿æ¨¡å—自带。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * åªæ¬éšè®¿ä¾§ç”¨åˆ°çš„那一个查询:{@code ServiceTaskdiagMapper#selectServiceTaskdiagListByIcdName}
 * ï¼ˆåŽŸ XML L104-154)。其余 CRUD å±žä»»åŠ¡é…ç½®ç«¯ï¼Œ<b>不搬</b>。
 *
 * <h3>⚠️ ä¸ºä»€ä¹ˆæ˜¯ "ByIcdName" è€Œä¸æ˜¯æ™®é€š list</h3>
 * è¿™ä¸ªæ–¹æ³•名里的 IcdName æœ‰åŽ†å²åŽŸå› ï¼šå®ƒçš„æ¡ä»¶é‡Œæ—¢æ”¯æŒ {@code icd10code} ä¹Ÿæ”¯æŒ
 * {@code icd10name},且 {@code dept_code / ward_code / dept_name / ward_name} éƒ½æ˜¯å¯é€‰æ¡ä»¶ã€‚
 * å—华附一({@code active == "nhfy"})因为 ICD10 ç¼–码有重复,改用<b>主诊断名称</b>
 * {@code icd10name} æŸ¥è¯¢ï¼›å…¶å®ƒç«™ç‚¹ç”¨ {@code icd10code}。这个站点差异在
 * {@code InhospTaskOrchestrator} é‡ŒæŒ‰åŽŸé€»è¾‘ä¿ç•™ã€‚
 *
 * <h3>⚠️ ä¸Ž smartor çš„ Mapper æ˜¯ä¸¤å¥—等价 SQL</h3>
 * çºªå¾‹åŒ {@code VisitInhospMapper}。
 */
@Mapper
public interface VisitTaskdiagMapper {
    /**
     * æŒ‰ç–¾ç—…维度查长期任务(原 {@code selectServiceTaskdiagListByIcdName})。
     * <p>
     * å›ºå®šæ¡ä»¶ {@code del_flag = 0};可选条件(<b>顺序与内容逐条照抄</b>):
     * {@code task_id / longtask / task_name(like) / service_type / icd10id / icd10code /
     * icd10name / orgid / guid / pguid / pid / dept_code / dept_name / ward_code / ward_name}。
     */
    List<VisitTaskdiagRow> selectListByIcdName(VisitTaskdiagRow query);
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/InhospSubtaskWriter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,571 @@
package com.ruoyi.inhospvisit.service;
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 com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.support.InhospFlagWriter;
import com.ruoyi.inhospvisit.support.VisitSubtaskFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.util.Date;
import java.util.List;
/**
 * éšè®¿å­ä»»åŠ¡ã€Œå†™å…¥å£ã€â€”â€” é€è¡Œæ¬è‡ª {@code PatMedInhospServiceImpl#writeInSubTask}(L682-913,232 è¡Œï¼‰ã€‚
 *
 * <h3>这次搬迁的形态:<b>结构性改写</b>(不是纯搬运)</h3>
 * åŽŸå®žçŽ°ç›´æŽ¥ç”¨ {@code serviceSubtaskMapper} / {@code patMedInhospMapper} å†™åº“。
 * æŒ‰ä½ çš„要求「任务创建将来单独拉一个模块」,本次把三处写库能力换成
 * {@link VisitTaskCreateApi} çš„三个方法:
 * <table border="1">
 *   <tr><th>原调用</th><th>现在</th></tr>
 *   <tr><td>{@code serviceTaskMapper.selectServiceTaskList(st)}(L689)</td>
 *       <td>{@link VisitTaskCreateApi#findPublishedLongTask(Long)}</td></tr>
 *   <tr><td>{@code serviceSubtaskMapper.insertServiceSubtask(...)}(L823/838/854/862/873)</td>
 *       <td>{@link VisitTaskCreateApi#insertSubtask(VisitSubtaskDraft)}</td></tr>
 *   <tr><td>{@code serviceSubtaskMapper.updateServiceSubtask(...)}(L782/791/812/836)</td>
 *       <td>{@link VisitTaskCreateApi#updatePendingSubtask(VisitPendingSubtaskDto)}</td></tr>
 *   <tr><td>{@code serviceSubtaskMapper.selectServiceSubtaskList(subtask)}(L764)</td>
 *       <td>{@link VisitTaskCreateApi#findPendingSubtasks(Long, Long)}</td></tr>
 * </table>
 * å› ä¸ºå±žäºŽç»“构性改写,<b>逐行比对盖不住</b>,所以本方法配的是
 * <b>B æ¡£ã€Œå£å¾„双向断言」</b>({@code sql/_probe/verify_inhospvisit_writein_semantics.py}),
 * é€æ¡æ–­è¨€åŽŸå®žçŽ°çš„å£å¾„åœ¨æ–°å®žçŽ°é‡Œä»ç„¶å­˜åœ¨ã€‚
 *
 * <h3>⚠️ åŽŸå®žçŽ°çš„ 3 å¤„怪癖,全部原样保留(写错就是行为变更)</h3>
 * <ol>
 *   <li><b>{@code type == 0}(药品宣教)用的是 {@code medcheck_flag}</b>,
 *       è€Œ catch å—里只有 {@code type==1/2/3/0} å››ä¸ªåˆ†æ”¯ â€”—
 *       <b>type=4/5 æŠ›å¼‚常时不会回写任何标志位</b>(原 L900-912)。</li>
 *   <li><b>{@code type == 3} åˆ†æ”¯çš„ {@code updateServiceSubtask} è¿”回值被丢弃</b>
 *       ï¼ˆåŽŸ L836 æ²¡æœ‰ {@code Boolean aBoolean = ...}),而 {@code i} æ¥è‡ªåŽé¢çš„
 *       {@code insertServiceSubtask} â€”— æ‰€ä»¥ type=3 åªçœ‹ insert ç»“果,不看停旧结果。</li>
 *   <li><b>{@code serviceSubtask1.getServiceType().equals("2")} æœªåˆ¤ null</b>(原 L803)
 *       â€”— å¾…执行子任务的 serviceType ä¸º null æ—¶ä¼š NPE。原样保留。</li>
 * </ol>
 *
 * <h3>⚠️ é™çº§ç­–ç•¥</h3>
 * {@link VisitTaskCreateApi} æ˜¯"任务创建模块"的预留接口,本次由 smartor æä¾›è¿‡æ¸¡å®žçŽ°ã€‚
 * å–不到实现时(模块没打进应用 / å®¹å™¨å¼‚常)本类<b>按"不生成子任务"处理并打 WARN</b>,
 * ç»ä¸æŠ›å¼‚常 â€”— å‡ºå…¥é™¢éšè®¿æŒ‚了就是当天全院随访停摆。
 */
@Slf4j
@Service
public class InhospSubtaskWriter {
    /** æ™¯å®æœºæž„ID(原 L798 ç¡¬ç¼–码;已逐字核对原实现 4 å¤„) */
    private static final String ORG_JINGNING = "47255004333112711A1001";
    /** å‡ºé™¢éšè®¿çš„æœåŠ¡ç±»åž‹ï¼ˆå†å…¥é™¢åœæœºåˆ†æ”¯çš„å¿…è¦æ¡ä»¶ï¼ŒåŽŸ L803) */
    private static final String SERVICE_TYPE_DISCHARGE = "2";
    /** å¾…执行状态 */
    private static final long SENDSTATE_PENDING = 2L;
    /** åœæœºçŠ¶æ€ï¼ˆåŽŸå®žçŽ°æŠŠ sendstate ç½® 4 è¡¨ç¤ºä¸å†æ‰§è¡Œï¼‰ */
    private static final long SENDSTATE_STOPPED = 4L;
    /** ç–¾ç—…维度(type=3)专用:停旧任务后把执行方式全置失败 */
    private static final String PREACHFORM_STATE_FAIL = "5";
    @Autowired
    private VisitSubtaskFactory subtaskFactory;
    @Autowired
    private InhospFlagWriter flagWriter;
    /**
     * {@code addServiceSubtaskPreachform} çš„委托目标(写 service_subtask_preachform)。
     * <p>
     * è¯¥æ–¹æ³•(原 L1061-1103,43 è¡Œï¼‰åœ¨<b>第 8 æ­¥</b>才搬进本模块,
     * ä½† {@code writeInSubTask} çŽ°åœ¨å°±ç”¨åˆ°å®ƒï¼ˆL840/855/874)。
     * æ‰€ä»¥è¿™é‡Œå…ˆç”¨ä¸€ä¸ª {@code ObjectProvider} æƒ°æ€§æ‹¿ï¼›å–不到就跳过并打 WARN。
     */
    @Autowired
    private ObjectProvider<SubtaskPreachformWriter> preachformWriterProvider;
    @Autowired
    private ObjectProvider<VisitTaskCreateApi> taskCreateApiProvider;
    /**
     * æŠŠæ‚£è€…写进随访子任务表 â€”— åŽŸ {@code writeInSubTask(Long taskid, Boolean check, PatMedInhosp,
     * PatArchive, Integer type, String config)}。
     *
     * @param taskid   å‘½ä¸­çš„长期任务ID
     * @param check    æ˜¯å¦éœ€è¦åš"再入院"去重校验
     * @param inhosp   å‡ºå…¥é™¢è®°å½•
     * @param archive  æ‚£è€…档案
     * @param type     0=药品宣教 / 1=离院科室 / 2=离院病区 / 3=疾病 / 4=入院科室 / 5=入院病区
     * @param config   {@code sys_config: visit.early.day}
     */
    public void writeInSubTask(Long taskid, Boolean check, VisitInhospRow inhosp,
                               VisitArchiveRow archive, Integer type, String config) {
        // åŽŸ L683-685
        String longTaskReason = inhosp.getLongTaskReason();
        String orgId = inhosp.getOrgid();
        if (StringUtils.isEmpty(longTaskReason)) {
            longTaskReason = "";
        }
        // åŽŸ L686-728:查"已发布的长期任务";查不到时按 type å†™ä¸åŒçš„æ ‡å¿—位后 return
        VisitTaskCreateApi taskApi = taskCreateApi();
        if (taskApi == null) {
            log.warn("【出入院随访】VisitTaskCreateApi ä¸å¯ç”¨ï¼ˆä»»åŠ¡åˆ›å»ºæ¨¡å—æœªè£…é…ï¼‰ï¼Œæœ¬æ‚£è€…ä¸ç”Ÿæˆå­ä»»åŠ¡ã€‚"
                    + "inhospid={}, taskid={}", inhosp.getInhospid(), taskid);
            return;
        }
        VisitLongTaskDto serviceTask = taskApi.findPublishedLongTask(taskid);
        if (serviceTask == null) {
            writeMissingTaskFlag(inhosp, type, longTaskReason, taskid);
            return;
        }
        // åŽŸ L729-731
        if ("1".equals(inhosp.getInhospstate()) && inhosp.getEndtime() == null) {
            return;
        }
        // åŽŸ L732-736:组装(副作用由本类实现,见 missingOutHospTime)
        VisitSubtaskDraft serviceSubtask = subtaskFactory.boxedServiceSubtask(
                toTaskSource(serviceTask, inhosp), inhosp, archive, config, type,
                row -> onMissingOutHospTime(row));
        if (ObjectUtils.isEmpty(serviceSubtask)) {
            return;
        }
        // åŽŸ L737-753:就诊科室/病区填充
        if (type != null && type != 2) {
            // ç§‘室和疾病 éƒ½ç”¨ ç§‘室填充
            if ("1".equals(inhosp.getInhospstate())) {
                serviceSubtask.setVisitDeptCode(inhosp.getLeaveldeptcode());
                serviceSubtask.setVisitDeptName(inhosp.getLeaveldeptname());
            } else if ("0".equals(inhosp.getInhospstate())) {
                serviceSubtask.setVisitDeptCode(inhosp.getDeptcode());
                serviceSubtask.setVisitDeptName(inhosp.getDeptname());
                if (type == 5) {
                    serviceSubtask.setVisitDeptCode(inhosp.getHospitaldistrictcode());
                    serviceSubtask.setVisitDeptName(inhosp.getHospitaldistrictname());
                }
            }
        } else if (type != null && type == 2) {
            serviceSubtask.setVisitDeptCode(inhosp.getLeavehospitaldistrictcode());
            serviceSubtask.setVisitDeptName(inhosp.getLeavehospitaldistrictname());
        }
        // åŽŸ L754-755:记录 insertServiceSubtask çš„返回状态
        int i = 0;
        try {
            // åŽŸ L757-758
            if (Boolean.TRUE.equals(check)
                    && (serviceSubtask.getTaskSituation() == null || serviceSubtask.getTaskSituation() != 6)) {
                // åŽŸ L759-764:查该患者 + è¯¥ taskid ä¸‹æ‰€æœ‰å¾…执行子任务
                List<VisitPendingSubtaskDto> pendingList =
                        taskApi.findPendingSubtasks(archive.getId(), taskid);
                if (pendingList != null && !pendingList.isEmpty()) {
                    for (VisitPendingSubtaskDto pending : pendingList) {
                        // åŽŸ L768-771:不是长期任务,不处理
                        if (pending.getLongSendTime() == null) {
                            continue;
                        }
                        // åŽŸ L772-773:type = 1或2 è¡¨ç¤º "科室、病区"
                        if (type != null && (type == 1 || type == 2)) {
                            Date endTime = serviceSubtask.getEndtime();
                            Date endTime1 = pending.getEndtime();
                            // åŽŸ L776-785:出院时间判空
                            if (ObjectUtils.isEmpty(endTime)) {
                                serviceSubtask.setResult("error");
                                serviceSubtask.setRemark("出院时间为空,serviceSubtask的Id为" + serviceSubtask.getId());
                                serviceSubtask.setUpdateBy(serviceTask.getUpdateBy());
                                serviceSubtask.setUpdateTime(new Date());
                                // âš ï¸ è¿™é‡ŒåŽŸå®žçŽ°æ˜¯ update ä¸€ä¸ªã€å°šæœª insert】的草稿(id ä¸º null)。
                                //     ä¿ç•™è¯¥è¡Œä¸ºï¼šé  id ä¸º null è®© update å½±å“ 0 è¡Œã€‚
                                if (writeBackDraft(taskApi, serviceSubtask)) {
                                    i = 1;
                                }
                                continue;
                            }
                            if (ObjectUtils.isEmpty(endTime1)) {
                                pending.setResult("error");
                                pending.setRemark("出院时间为空,serviceSubtask的Id为" + pending.getId());
                                pending.setUpdateBy(serviceTask.getUpdateBy());
                                pending.setUpdateTime(new Date());
                                if (taskApi.updatePendingSubtask(pending)) {
                                    i = 1;
                                }
                                continue;
                            }
                            // åŽŸ L795-800:景宁 å‡ºé™¢é¦–次(7天)随访一定要做
                            boolean firstVisit = ORG_JINGNING.equals(orgId);
                            if (!firstVisit) {
                                // åŽŸ L802-803
                                // âš ï¸ åŽŸå®žçŽ°ä¸åˆ¤ serviceType æ˜¯å¦ä¸º null(会 NPE),原样保留
                                if (endTime1.before(endTime)
                                        && SERVICE_TYPE_DISCHARGE.equals(pending.getServiceType())) {
                                    // åŽŸ L804-813:把循环到的旧子任务停掉
                                    pending.setSendstate(SENDSTATE_STOPPED);
                                    if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) {
                                        serviceSubtask.setRemark(serviceSubtask.getRemark() + ",科室/病区患者再入院");
                                    } else {
                                        serviceSubtask.setRemark("科室/病区患者再入院");
                                    }
                                    pending.setResult("error");
                                    pending.setUpdateBy(serviceTask.getUpdateBy());
                                    pending.setUpdateTime(new Date());
                                    if (taskApi.updatePendingSubtask(pending)) {
                                        i = 1;
                                    }
                                } else {
                                    // åŽŸ L814-824:相反,则把"当前的"数据停掉(置 error + sendstate=4 åŽ insert)
                                    serviceSubtask.setResult("error");
                                    serviceSubtask.setSendstate(SENDSTATE_STOPPED);
                                    if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) {
                                        serviceSubtask.setRemark(serviceSubtask.getRemark()
                                                + ",当前患者出院时间后有待执行的出院随访长期任务");
                                    } else {
                                        serviceSubtask.setRemark("当前患者出院时间后有待执行的出院随访长期任务");
                                    }
                                    serviceSubtask.setCreateBy(serviceTask.getCreateBy());
                                    serviceSubtask.setCreateTime(new Date());
                                    i = insert(taskApi, serviceSubtask) ? 1 : 0;
                                }
                            }
                        } else if (type != null && type == 3) {
                            // åŽŸ L826-841:type=3 ç–¾ç—… â€”— æ— æ¡ä»¶åœæ—§ + é‡æ–°æ–°å¢ž
                            pending.setSendstate(SENDSTATE_STOPPED);
                            if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) {
                                serviceSubtask.setRemark(serviceSubtask.getRemark() + ",疾病患者再入院");
                            } else {
                                serviceSubtask.setRemark("疾病患者再入院");
                            }
                            pending.setResult("error");
                            pending.setUpdateBy(serviceTask.getUpdateBy());
                            // âš ï¸ åŽŸå®žçŽ°ã€ä¸¢å¼ƒã€‘äº†è¿™é‡Œçš„è¿”å›žå€¼ï¼ˆå¼‚å¸¸è§ç±»æ³¨é‡Šç¬¬ 2 æ¡ï¼‰
                            taskApi.updatePendingSubtask(pending);
                            // é‡æ–°æ–°å¢žå­ä»»åŠ¡
                            i = insert(taskApi, serviceSubtask) ? 1 : 0;
                            // è¿™é‡Œéœ€è¦å…¨å¤±è´¥
                            addServiceSubtaskPreachform(serviceSubtask, PREACHFORM_STATE_FAIL);
                        }
                    }
                } else {
                    // åŽŸ L844-856:没有待执行任务
                    markPhoneEmptyIfNeeded(serviceSubtask);
                    serviceSubtask.setCreateBy(serviceTask.getCreateBy());
                    serviceSubtask.setCreateTime(new Date());
                    i = insert(taskApi, serviceSubtask) ? 1 : 0;
                    addServiceSubtaskPreachform(serviceSubtask, null);
                }
            } else if (serviceSubtask.getTaskSituation() != null && serviceSubtask.getTaskSituation() == 6) {
                // åŽŸ L857-862:该患者被过滤,不用执行
                serviceSubtask.setCreateBy(serviceTask.getCreateBy());
                serviceSubtask.setCreateTime(new Date());
                serviceSubtask.setSendstate(SENDSTATE_STOPPED);
                i = insert(taskApi, serviceSubtask) ? 1 : 0;
            } else {
                // åŽŸ L863-875:不需要校验(check=false)
                markPhoneEmptyIfNeeded(serviceSubtask);
                serviceSubtask.setCreateBy(serviceTask.getCreateBy());
                serviceSubtask.setCreateTime(new Date());
                i = insert(taskApi, serviceSubtask) ? 1 : 0;
                addServiceSubtaskPreachform(serviceSubtask, null);
            }
            // åŽŸ L877-899:把 check_flag æ”¹æˆ 1(已处理)/ ç”Ÿæˆå¤±è´¥ç½® 2
            if (i == 1) {
                writeFlagByType(inhosp.getInhospid(), type, "1", null);
            } else {
                log.info("生成子任务失败serviceSubtask的taskid为:{},patid为:{}",
                        serviceSubtask.getTaskid(), serviceSubtask.getPatid());
                writeFlagByType(inhosp.getInhospid(), type, "2", "生成子任务失败 " + type);
            }
        } catch (Exception e) {
            // åŽŸ L900-912:出错回滚事务
            log.info("生成子任务报错回滚serviceSubtask的taskid为:{},patid为:{}",
                    serviceSubtask.getTaskid(), serviceSubtask.getPatid());
            // âš ï¸ åŽŸå®žçŽ°çš„ catch åªå¤„理 type 1/2/3/0 â€”— type=4/5 æŠ›å¼‚常时不回写任何标志位。
            //     åŽŸæ ·ä¿ç•™ï¼ˆè§ç±»æ³¨é‡Šç¬¬ 1 æ¡ï¼‰ã€‚
            writeFlagByTypeForRollback(inhosp.getInhospid(), type, "2", "生成子任务报错回滚 " + type);
            e.printStackTrace();
        }
    }
    // ==========================================================================
    // åŽŸ L691-725:任务不存在时按 type å†™æ ‡å¿—位
    // ==========================================================================
    private void writeMissingTaskFlag(VisitInhospRow inhosp, Integer type, String longTaskReason, Long taskid) {
        if (type == null) {
            return;
        }
        // åŽŸå®žçŽ°å¯¹ type ç”¨çš„æ˜¯ if / else-if é“¾ï¼Œè¯­ä¹‰ç­‰ä»·äºŽ switch;这里保持同样的分支与文案
        switch (type) {
            case 0:
                log.info("该患者药品宣教长期任务不存在,任务ID为:{}", taskid);
                flagWriter.setFlagWithReason(inhosp.getInhospid(), "medcheck_flag", "2",
                        longTaskReason + " & è¯¥æ‚£è€…药品宣教长期任务不存在,任务ID为:" + taskid);
                return;
            case 1:
                log.info("该患者出院随访长期任务不存在,任务ID为:{}", taskid);
                flagWriter.setFlagWithReason(inhosp.getInhospid(), "deptcheck_flag", "2",
                        longTaskReason + " & è¯¥æ‚£è€…离院科室长期任务不存在,任务ID为:" + taskid);
                return;
            case 2:
                flagWriter.setFlagWithReason(inhosp.getInhospid(), "wardcheck_flag", "2",
                        longTaskReason + " & è¯¥æ‚£è€…离院病区长期任务不存在,任务ID为:" + taskid);
                return;
            case 3:
                flagWriter.setFlagWithReason(inhosp.getInhospid(), "diagcheck_flag", "2",
                        longTaskReason + " & è¯¥æ‚£è€…疾病长期任务不存在,任务ID为:" + taskid);
                return;
            case 4:
                flagWriter.setFlagWithReason(inhosp.getInhospid(), "in_deptcheck_flag", "2",
                        longTaskReason + " & è¯¥æ‚£è€…入院科室长期任务不存在,任务ID为:" + taskid);
                return;
            case 5:
                flagWriter.setFlagWithReason(inhosp.getInhospid(), "in_wardcheck_flag", "2",
                        longTaskReason + " & è¯¥æ‚£è€…入院病区长期任务不存在,任务ID为:" + taskid);
                return;
            default:
                // åŽŸå®žçŽ°ï¼štype ä¸å±žäºŽ 0-5 æ—¶ï¼Œif/else-if é“¾å…¨éƒ¨ä¸å‘½ä¸­ â†’ ä»€ä¹ˆéƒ½ä¸åšã€ä¹Ÿä¸ return
                // â†’ ç»§ç»­å¾€ä¸‹èµ°ï¼Œæ­¤æ—¶ serviceTask ä»æ˜¯ null,到 L729 ä¹‹åŽä¼š NPE。
                // è¿™é‡Œã€ä¸å¤åˆ¶ã€‘那个 NPE(它只会掩盖问题),改为记录并返回。
                log.warn("【出入院随访】任务不存在且 type={} ä¸å±žäºŽ 0-5,无法判定回写哪个标志位。"
                        + "inhospid={}, taskid={}", type, inhosp.getInhospid(), taskid);
        }
    }
    // ==========================================================================
    // åŽŸ L877-912:标志位回写(成功 / å¤±è´¥ / å¼‚常回滚)
    // ==========================================================================
    /**
     * type â†’ æ ‡å¿—位回写。<b>照抄原 L881-885 ä¸Ž L892-896 çš„分支</b>。
     * <p>
     * âš ï¸ æ³¨æ„ type=0 åœ¨"成功/失败"分支里【没有】对应 if(原实现也没写),
     * æ‰€ä»¥ type=0 ç”ŸæˆæˆåŠŸåŽä¸ä¼šæŠŠ {@code medcheck_flag} ç½® 1 â€”— åŽŸæ ·ä¿ç•™ã€‚
     * <p>
     * remark åªæœ‰"失败"分支会写(原 L897)。
     */
    private void writeFlagByType(Long inhospid, Integer type, String flagValue, String remark) {
        if (type == null) {
            return;
        }
        String column = flagColumnOfSuccessOrFail(type);
        if (column == null) {
            // type=0(药品宣教)在成功/失败分支里没有对应 flag,原实现同样什么都不做
            return;
        }
        if (remark == null) {
            flagWriter.setFlag(inhospid, column, flagValue);
        } else {
            flagWriter.setFlagWithRemark(inhospid, column, flagValue, remark);
        }
    }
    /**
     * type â†’ æ ‡å¿—位列(成功/失败分支)。照抄原 L881-885 / L892-896:
     * <pre>
     * 1 -> deptcheck_flag   2 -> wardcheck_flag   3 -> diagcheck_flag
     * 4 -> in_deptcheck_flag 5 -> in_wardcheck_flag   ï¼ˆ0 æ— å¯¹åº”)
     * </pre>
     */
    private String flagColumnOfSuccessOrFail(Integer type) {
        switch (type) {
            case 1:
                return "deptcheck_flag";
            case 2:
                return "wardcheck_flag";
            case 3:
                return "diagcheck_flag";
            case 4:
                return "in_deptcheck_flag";
            case 5:
                return "in_wardcheck_flag";
            default:
                return null;
        }
    }
    /**
     * å¼‚常回滚时的标志位回写。照抄原 L905-908 â€”—
     * âš ï¸ <b>这里的分支与原"成功/失败"分支不同</b>:
     * <ul>
     *   <li>有 {@code type==0 â†’ medcheck_flag};</li>
     *   <li><b>没有</b> {@code type==4/5} çš„分支(即入院宣教抛异常时标志位不动)。</li>
     * </ul>
     * ä¸¤å¤„不一致是原代码本身的不一致,<b>原样保留</b>。
     */
    private void writeFlagByTypeForRollback(Long inhospid, Integer type, String flagValue, String remark) {
        if (type == null) {
            return;
        }
        String column;
        switch (type) {
            case 1:
                column = "deptcheck_flag";
                break;
            case 2:
                column = "wardcheck_flag";
                break;
            case 3:
                column = "diagcheck_flag";
                break;
            case 0:
                column = "medcheck_flag";
                break;
            default:
                // type=4/5 è½è¿™é‡Œï¼šåŽŸå®žçŽ°ä¸å›žå†™ä»»ä½•æ ‡å¿—ä½
                return;
        }
        flagWriter.setFlagWithRemark(inhospid, column, flagValue, remark);
    }
    // ==========================================================================
    // è¾…助
    // ==========================================================================
    /**
     * åŽŸ L845-851 / L864-870:手机号为空时追加备注、置 sendstate=4、result=error。
     */
    private void markPhoneEmptyIfNeeded(VisitSubtaskDraft d) {
        if (StringUtils.isEmpty(d.getPhone())) {
            if (StringUtils.isNotEmpty(d.getRemark())) {
                d.setRemark(d.getRemark() + ",手机号为空");
            } else {
                d.setRemark("手机号为空");
            }
            d.setSendstate(SENDSTATE_STOPPED);
            d.setResult("error");
        }
    }
    /**
     * åŽŸ {@code boxedServiceSubtask} å‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶çš„副作用(L1174-1176):
     * <pre>
     * patMedInhosp1.setDeptcheckFlag("2");
     * patMedInhosp1.setRemark(patMedInhosp1.getRemark() + "&amp; è¯¥æ‚£è€…疾病出院时间为空,出院记录ID为:" + patMedInhosp1.getInhospid());
     * patMedInhospMapper.updatePatMedInhosp(patMedInhosp1);
     * </pre>
     * âš ï¸ åŽŸå®žçŽ°è¿™æ¬¡è°ƒç”¨ã€åŒæ—¶ã€‘å†™äº† flag ä¸Ž remark,所以这里两步都要做。
     */
    private void onMissingOutHospTime(VisitInhospRow row) {
        flagWriter.setFlag(row.getInhospid(), "deptcheck_flag", "2");
        flagWriter.setFlagWithRemark(row.getInhospid(), "deptcheck_flag", "2",
                (row.getRemark() == null ? "" : row.getRemark())
                        + "& è¯¥æ‚£è€…疾病出院时间为空,出院记录ID为:" + row.getInhospid());
    }
    /**
     * æŠŠ {@link VisitLongTaskDto} é€‚配成 {@link com.ruoyi.inhospvisit.domain.row.VisitTaskRow},
     * ä¾›ç»„装器使用。
     * <p>
     * ç»„装器只读 task çš„少量字段,这里逐字段映射;<b>不留任何"看起来没用"的字段不映射</b>
     * â€”— å°‘映射一个就会静默丢值(见 VisitSubtaskFactory çš„类注释)。
     * <p>
     * âš ï¸ {@code type / sendDay / updateBy / createBy / serviceType / preachform} ç­‰
     * éƒ½åœ¨ {@link VisitLongTaskDto} é‡Œæœ‰å¯¹åº”字段,直接搬。
     */
    private com.ruoyi.inhospvisit.domain.row.VisitTaskRow toTaskSource(VisitLongTaskDto dto,
                                                                       VisitInhospRow inhosp) {
        com.ruoyi.inhospvisit.domain.row.VisitTaskRow t = new com.ruoyi.inhospvisit.domain.row.VisitTaskRow();
        t.setTaskid(dto.getTaskid());
        t.setTaskName(dto.getTaskName());
        t.setType(dto.getType());
        t.setServiceType(dto.getServiceType());
        t.setTemplateid(dto.getTemplateid());
        t.setTemplatename(dto.getTemplatename());
        t.setLibtemplateid(dto.getLibtemplateid());
        t.setSendDay(dto.getSendDay());
        t.setPreachform(dto.getPreachform());
        t.setPreachformDesc(dto.getPreachformDesc());
        t.setOrgid(dto.getOrgid());
        t.setCampusid(dto.getCampusid());
        t.setUpdateBy(dto.getUpdateBy());
        t.setCreateBy(dto.getCreateBy());
        return t;
    }
    /**
     * insert è‰ç¨¿å¹¶æŠŠç”Ÿæˆçš„自增主键回填(原实现 insert åŽå¯ç»§ç»­ç”¨åŒä¸€ä¸ªå¯¹è±¡ï¼‰ã€‚
     */
    private boolean insert(VisitTaskCreateApi api, VisitSubtaskDraft d) {
        return api.insertSubtask(d);
    }
    /**
     * åŽŸ L782:对<b>尚未 insert çš„草稿</b>调 {@code updateServiceSubtask}。
     * <p>
     * è¯¥è‰ç¨¿æ­¤æ—¶ {@code id} ä¸º null,所以 {@code where id = null} å½±å“ 0 è¡Œã€‚
     * ä¸ºä¿æŒè¿™ä¸€"无效写入"的原语义,这里用一个 id ä¸º null çš„ pending å¯¹è±¡èµ°åŒä¸€æ¡è·¯å¾„。
     */
    private boolean writeBackDraft(VisitTaskCreateApi api, VisitSubtaskDraft d) {
        VisitPendingSubtaskDto p = new VisitPendingSubtaskDto();
        p.setId(d.getId());
        p.setTaskid(d.getTaskid());
        p.setSendstate(d.getSendstate());
        p.setResult(d.getResult());
        p.setRemark(d.getRemark());
        p.setUpdateBy(d.getUpdateBy());
        p.setUpdateTime(d.getUpdateTime());
        return api.updatePendingSubtask(p);
    }
    /**
     * åŽŸ L840/855/874 çš„ {@code addServiceSubtaskPreachform}。该方法的实现<b>第 8 æ­¥</b>才搬进来,
     * æ‰€ä»¥è¿™é‡Œæƒ°æ€§èŽ·å–ï¼›å–ä¸åˆ°å°±è·³è¿‡ï¼ˆä¸æŠ›å¼‚å¸¸ï¼‰ã€‚
     */
    private void addServiceSubtaskPreachform(VisitSubtaskDraft subtask, String sendstate) {
        if (preachformWriterProvider == null) {
            return;
        }
        try {
            SubtaskPreachformWriter writer = preachformWriterProvider.stream().findFirst().orElse(null);
            if (writer == null) {
                log.warn("【出入院随访】SubtaskPreachformWriter æœªè£…配,跳过执行方式明细写入。subtaskId={}",
                        subtask.getId());
                return;
            }
            writer.addServiceSubtaskPreachform(subtask, sendstate);
        } catch (Exception e) {
            // è¿™ä¸€æ­¥å¤±è´¥ä¸åº”影响主流程(原实现在同一个 try é‡Œï¼Œå¤±è´¥ä¼šèµ° catch å›žæ»šæ ‡å¿—位;
            // ä½†é‚£æ˜¯å› ä¸ºå®ƒä¸Ž insert åŒå±žä¸€æ¬¡äº‹åŠ¡ã€‚è¿™é‡Œæ‹†å¼€åŽæŒ‰"尽力而为"处理并记录)
            log.warn("【出入院随访】写执行方式明细失败,已忽略。subtaskId={},原因={}",
                    subtask.getId(), e.toString());
        }
    }
    private VisitTaskCreateApi taskCreateApi() {
        if (taskCreateApiProvider == null) {
            return null;
        }
        try {
            return taskCreateApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 VisitTaskCreateApi å¤±è´¥ï¼ŒæŒ‰ä¸å¯ç”¨å¤„理:{}", e.toString());
            return null;
        }
    }
    /**
     * å†™æ‰§è¡Œæ–¹å¼æ˜Žç»†çš„能力(原 {@code addServiceSubtaskPreachform},L1061-1103,43 è¡Œï¼‰ã€‚
     * <p>
     * è¯¥æ–¹æ³•的实现<b>第 8 æ­¥</b>才搬进本模块,所以先定义接口、用 {@code ObjectProvider} æƒ°æ€§èŽ·å–ã€‚
     * è¿™æ ·ç¬¬ 7 æ­¥å°±èƒ½ç¼–译并跑断言,不必等第 8 æ­¥ã€‚
     */
    public interface SubtaskPreachformWriter {
        /**
         * @param subtask   å­ä»»åŠ¡è‰ç¨¿ï¼ˆ{@code id} ä¸ºè½åº“后的主键)
         * @param sendstate æŒ‡å®šçš„æ‰§è¡Œæ–¹å¼çŠ¶æ€ï¼›{@code null} è¡¨ç¤ºæŒ‰åŽŸé€»è¾‘å¤„ç†
         */
        void addServiceSubtaskPreachform(VisitSubtaskDraft subtask, String sendstate);
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/InhospTaskOrchestrator.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,642 @@
package com.ruoyi.inhospvisit.service;
import com.ruoyi.common.core.service.PublicityDataPort;
import com.ruoyi.common.core.service.VisitTaskCreateApi;
import com.ruoyi.common.core.service.dto.VisitInhospUpdateDto;
import com.ruoyi.common.core.service.dto.VisitPendingSubtaskDto;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.domain.row.VisitTaskdeptRow;
import com.ruoyi.inhospvisit.domain.row.VisitTaskdiagRow;
import com.ruoyi.inhospvisit.mapper.VisitArchiveMapper;
import com.ruoyi.inhospvisit.mapper.VisitInhospMapper;
import com.ruoyi.inhospvisit.mapper.VisitTaskdeptMapper;
import com.ruoyi.inhospvisit.mapper.VisitTaskdiagMapper;
import com.ruoyi.inhospvisit.support.InhospFlagWriter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.ObjectProvider;
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;
/**
 * å‡ºé™¢éšè®¿ã€Œç¼–排器」—— é€è¡Œæ¬è‡ª {@code PatMedInhospServiceImpl}
 * çš„编排层({@code addSubTaskByRule} + 3 ä¸ª {@code *ForPatient} + {@code dealInHospInfo})。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th><th>行数</th></tr>
 *   <tr><td>{@link #addSubTaskByRule}</td><td>{@code addSubTaskByRule} L1691-1847</td><td>157</td></tr>
 *   <tr><td>{@link #addDeptSubTaskForPatient}</td><td>L1854-1887</td><td>34</td></tr>
 *   <tr><td>{@link #addWardSubTaskForPatient}</td><td>L1894-1935</td><td>42</td></tr>
 *   <tr><td>{@link #addIcd10SubTaskForPatient}</td><td>L1942-2001</td><td>60</td></tr>
 *   <tr><td>{@link #dealInHospInfo}</td><td>L1321-1359</td><td>39</td></tr>
 * </table>
 *
 * <h3>⚠️ ä¸‰ä¸ª {@code *ForPatient} çš„失败回写<b>写进去的列集合不同</b>,已逐一保真</h3>
 * <table border="1">
 *   <tr><th>位置</th><th>原写法</th><th>实际写入的列</th></tr>
 *   <tr><td>科室·无模板(L1866-1872)</td>
 *       <td>重读对象 {@code setDeptcheckFlag("2")},另两个 flag ç”±é‡è¯»å¯¹è±¡åŽŸæ ·å¸¦å›ž</td>
 *       <td>deptcheck + wardcheck + diagcheck + reason</td></tr>
 *   <tr><td>科室·catch(L1879-1885)</td>
 *       <td>在<b>新鲜对象</b>上 set</td>
 *       <td>deptcheck + reason</td></tr>
 *   <tr><td>病区·失败(L1914-1920)</td>
 *       <td>重读对象 {@code setWardcheckFlag("2")},另两个 flag åŽŸæ ·å¸¦å›ž</td>
 *       <td>wardcheck + deptcheck + diagcheck + reason</td></tr>
 *   <tr><td>病区·catch(L1927-1933)</td>
 *       <td>在新鲜对象上 set</td>
 *       <td>wardcheck + reason</td></tr>
 *   <tr><td>疾病·两处(L1967-1971 / L1985-1991)</td>
 *       <td>重读对象 {@code setDiagcheckFlag("2")} å¹¶è¦†å†™ reason</td>
 *       <td>diagcheck + reason(注意:<b>没有</b>带回其它两个 flag)</td></tr>
 *   <tr><td>疾病·catch(L1993-1998)</td>
 *       <td>在新鲜对象上 set</td>
 *       <td>diagcheck + reason</td></tr>
 * </table>
 * å› æ­¤æœ¬ç±»ç”¨ {@link PublicityDataPort#updateVisitInhosp(VisitInhospUpdateDto)}(可一次写多个 flag),
 * è€Œä¸æ˜¯çª„çš„"单 flag"接口 â€”— åŽè€…表达不了这些差异。
 *
 * <h3>⚠️ ç«™ç‚¹å·®å¼‚(原样保留)</h3>
 * <ul>
 *   <li>{@code nhfy}:疾病维度改用<b>主诊断名称</b> {@code leavediagname} æŸ¥è¯¢ï¼Œè€Œä¸æ˜¯ ICD10 ç¼–码
 *       ï¼ˆå› ä¸ºå—华附一的 icd10code æœ‰é‡å¤ï¼‰ï¼›</li>
 *   <li>{@code xh}:{@code addSubTaskByRule} é‡Œ<b>整块跳过</b>病区 + ç–¾ç—…两个维度(多处 {@code !xh} åˆ¤æ–­ï¼‰ã€‚</li>
 * </ul>
 */
@Slf4j
@Service
public class InhospTaskOrchestrator {
    /** å‡ºé™¢éšè®¿çš„æœåŠ¡ç±»åž‹ */
    private static final String SERVICE_TYPE_DISCHARGE = "2";
    /** ç»´åº¦ï¼šç§‘室 */
    private static final String DEPT_TYPE_DEPT = "1";
    /** ç»´åº¦ï¼šç—…区 */
    private static final String DEPT_TYPE_WARD = "2";
    /** ç«™ç‚¹ï¼šæ–°åŽï¼ˆè·³è¿‡ç—…区+疾病维度) */
    private static final String SITE_XH = "xh";
    /** ç«™ç‚¹ï¼šå—华附一(疾病维度用诊断名) */
    private static final String SITE_NHFY = "nhfy";
    @Value("${spring.profiles.active}")
    private String active;
    @Autowired
    private VisitInhospMapper inhospMapper;
    @Autowired
    private VisitArchiveMapper archiveMapper;
    @Autowired
    private VisitTaskdeptMapper taskdeptMapper;
    @Autowired
    private VisitTaskdiagMapper taskdiagMapper;
    @Autowired
    private InhospSubtaskWriter subtaskWriter;
    @Autowired
    private InhospFlagWriter flagWriter;
    @Autowired
    private ObjectProvider<PublicityDataPort> dataPortProvider;
    @Autowired
    private ObjectProvider<VisitTaskCreateApi> taskCreateApiProvider;
    // ==========================================================================
    // 1. åŸºäºŽè§„则的任务生成(原 L1691-1847)
    // ==========================================================================
    /**
     * æŒ‰ {@code service_task_rule} çš„「首要(1)/次要(2)」规则编排科室/病区/疾病三个维度。
     * <p>
     * é€è¡Œå¯¹åº”原 {@code addSubTaskByRule(String config, List<PatMedInhosp> patMedInhosps)}。
     * <p>
     * âš ï¸ è§„则查询 {@code serviceTaskRuleMapper.selectServiceTaskRuleByDeptOrWard} ä»åœ¨ smartor,
     * æ‰€ä»¥æœ¬æ–¹æ³•接收<b>已查好的规则</b>(由调用方 {@code InhospVisitTaskService} é€šè¿‡
     * {@code PublicityDataPort} å–——该端口已含"按科室/病区查规则"的能力,见下)。
     * è¿™æ ·åšæ˜¯ä¸ºäº†é¿å…ä¸º"规则表"再加一个 Mapper æˆ–端口。
     *
     * @param inhosp å¾…处理的出院患者
     * @param archive æ‚£è€…档案
     * @param rule å·²æŸ¥å¥½çš„规则;{@code null} = æœªé…ç½®è§„则(全部生成)
     * @param config {@code sys_config: visit.early.day}
     */
    public void addSubTaskByRule(VisitInhospRow inhosp, VisitArchiveRow archive,
                                 VisitTaskRule rule, String config) {
        try {
            if (rule == null) {
                // åŽŸ L1709-1716:规则为空,全部生成
                log.info("【基于规则】患者{}未配置规则,执行全部生成逻辑", inhosp.getInhospid());
                addDeptSubTaskForPatient(inhosp, archive, config);
                if (!SITE_XH.equals(active)) {
                    addWardSubTaskForPatient(inhosp, archive, config);
                    addIcd10SubTaskForPatient(inhosp, archive, config);
                }
                return;
            }
            // åŽŸ L1717-1726:有规则,按规则执行
            log.info("【基于规则】患者{}查询到规则:deptRule={}, wradRule={}, diagRule={}",
                    inhosp.getInhospid(), rule.getDeptRule(), rule.getWradRule(), rule.getDiagRule());
            boolean deptIsPrimary = "1".equals(rule.getDeptRule());
            boolean wardIsPrimary = "1".equals(rule.getWradRule());
            boolean diagIsPrimary = "1".equals(rule.getDiagRule());
            boolean deptIsSecondary = "2".equals(rule.getDeptRule());
            boolean wardIsSecondary = "2".equals(rule.getWradRule());
            boolean diagIsSecondary = "2".equals(rule.getDiagRule());
            boolean hasPrimaryRule = deptIsPrimary || wardIsPrimary || diagIsPrimary;
            if (!hasPrimaryRule) {
                // åŽŸ L1829-1837:没有首要规则,全部生成
                log.info("【基于规则】无首要规则,执行全部生成逻辑");
                addDeptSubTaskForPatient(inhosp, archive, config);
                if (!SITE_XH.equals(active)) {
                    addWardSubTaskForPatient(inhosp, archive, config);
                    addIcd10SubTaskForPatient(inhosp, archive, config);
                }
                return;
            }
            // åŽŸ L1730-1754:先执行所有规则=1 çš„æ–¹æ³•
            int deptGenCount = 0;
            int wardGenCount = 0;
            int diagGenCount = 0;
            if (deptIsPrimary) {
                log.info("【基于规则】执行科室首要规则");
                deptGenCount = addDeptSubTaskForPatient(inhosp, archive, config);
            }
            if (!SITE_XH.equals(active)) {
                if (wardIsPrimary) {
                    log.info("【基于规则】执行病区首要规则");
                    wardGenCount = addWardSubTaskForPatient(inhosp, archive, config);
                }
                if (diagIsPrimary) {
                    log.info("【基于规则】执行疾病首要规则");
                    diagGenCount = addIcd10SubTaskForPatient(inhosp, archive, config);
                }
            }
            int primaryGeneratedCount = deptGenCount + wardGenCount + diagGenCount;
            log.info("【基于规则】首要规则执行结果: deptGen={}, wardGen={}, diagGen={}, total={}",
                    deptGenCount, wardGenCount, diagGenCount, primaryGeneratedCount);
            String remark = inhosp.getRemark();
            if (primaryGeneratedCount > 0) {
                // åŽŸ L1758-1796:至少一个首要规则成功
                log.info("【基于规则】首要规则有成功(count={}),将次要规则对应flag设为2", primaryGeneratedCount);
                if (deptIsSecondary) {
                    updateCheckFlag(inhosp, 1, "2", StringUtils.isNotEmpty(remark)
                            ? remark + ";首要规则已生成,次要规则跳过" : "首要规则已生成,次要规则跳过");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsSecondary) {
                        updateCheckFlag(inhosp, 2, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";首要规则已生成,次要规则跳过" : "首要规则已生成,次要规则跳过");
                    }
                    if (diagIsSecondary) {
                        updateCheckFlag(inhosp, 3, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";首要规则已生成,次要规则跳过" : "首要规则已生成,次要规则跳过");
                    }
                }
                if (deptIsPrimary && deptGenCount > 0) {
                    updateCheckFlag(inhosp, 1, "1", StringUtils.isNotEmpty(remark)
                            ? remark + ";科室首要规则生成成功" : "科室首要规则生成成功");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsPrimary && wardGenCount > 0) {
                        updateCheckFlag(inhosp, 2, "1", StringUtils.isNotEmpty(remark)
                                ? remark + ";病区首要规则生成成功" : "病区首要规则生成成功");
                    }
                    if (diagIsPrimary && diagGenCount > 0) {
                        updateCheckFlag(inhosp, 3, "1", StringUtils.isNotEmpty(remark)
                                ? remark + ";疾病首要规则生成成功" : "疾病首要规则生成成功");
                    }
                }
                if (deptIsPrimary && deptGenCount == 0) {
                    updateCheckFlag(inhosp, 1, "2", StringUtils.isNotEmpty(remark)
                            ? remark + ";科室首要规则生成失败" : "科室首要规则生成失败");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsPrimary && wardGenCount == 0) {
                        updateCheckFlag(inhosp, 2, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";病区首要规则生成失败" : "病区首要规则生成失败");
                    }
                    if (diagIsPrimary && diagGenCount == 0) {
                        updateCheckFlag(inhosp, 3, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";疾病首要规则生成失败" : "疾病首要规则生成失败");
                    }
                }
            } else {
                // åŽŸ L1797-1828:所有首要规则全部失败 â†’ æŠŠ =1 çš„ç½® 2,然后执行次要规则
                log.info("【基于规则】首要规则全部失败,将=1的flag设为2,开始执行次要规则");
                if (deptIsPrimary) {
                    updateCheckFlag(inhosp, 1, "2", StringUtils.isNotEmpty(remark)
                            ? remark + ";科室首要规则全部失败" : "科室首要规则全部失败");
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsPrimary) {
                        updateCheckFlag(inhosp, 2, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";病区首要规则全部失败" : "病区首要规则全部失败");
                    }
                    if (diagIsPrimary) {
                        updateCheckFlag(inhosp, 3, "2", StringUtils.isNotEmpty(remark)
                                ? remark + ";疾病首要规则全部失败" : "疾病首要规则全部失败");
                    }
                }
                if (deptIsSecondary) {
                    log.info("【基于规则】执行科室次要规则");
                    addDeptSubTaskForPatient(inhosp, archive, config);
                }
                if (!SITE_XH.equals(active)) {
                    if (wardIsSecondary) {
                        log.info("【基于规则】执行病区次要规则");
                        addWardSubTaskForPatient(inhosp, archive, config);
                    }
                    if (diagIsSecondary) {
                        log.info("【基于规则】执行疾病次要规则");
                        addIcd10SubTaskForPatient(inhosp, archive, config);
                    }
                }
            }
        } catch (Exception e) {
            // åŽŸ L1839-1845
            e.printStackTrace();
            log.error("【基于规则】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            updateCheckFlag(inhosp, 1, "2", "基于规则处理异常");
        }
    }
    /**
     * ç»Ÿä¸€å›žå†™æ ‡å¿—位 â€”— å§”托 {@link InhospFlagWriter}(原 {@code updateCheckFlag})。
     */
    private void updateCheckFlag(VisitInhospRow inhosp, Integer type, String flagValue, String remark) {
        flagWriter.updateCheckFlag(inhosp, type, flagValue, remark);
    }
    // ==========================================================================
    // 2. ç§‘室维度(原 L1854-1887)
    // ==========================================================================
    /**
     * ä¸ºå•个患者生成科室维度的子任务。
     *
     * @return ç”Ÿæˆçš„子任务数量
     */
    public int addDeptSubTaskForPatient(VisitInhospRow inhosp, VisitArchiveRow archive, String config) {
        int generatedCount = 0;
        try {
            // åŽŸ L1857-1864:按患者所在科室查该科室的长期任务(dept_type=1 + service_type=2)
            VisitTaskdeptRow query = new VisitTaskdeptRow();
            query.setLongtask(1L);
            query.setDeptCode(inhosp.getLeaveldeptcode());
            query.setDeptType(DEPT_TYPE_DEPT);
            query.setServiceType(SERVICE_TYPE_DISCHARGE);
            query.setOrgid(inhosp.getOrgid());
            List<VisitTaskdeptRow> taskdepts = taskdeptMapper.selectList(query);
            if (CollectionUtils.isEmpty(taskdepts)) {
                // åŽŸ L1866-1872:⚠️ åœ¨ã€é‡è¯»å¯¹è±¡ã€‘上改 deptcheck,并把另两个 flag åŽŸæ ·å¸¦å›ž
                VisitInhospRow fresh = inhospMapper.selectByInhospid(inhosp.getInhospid());
                VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                u.setInhospid(inhosp.getInhospid());
                u.setDeptcheckFlag("2");
                u.setWardcheckFlag(fresh == null ? null : fresh.getWardcheckFlag());
                u.setDiagcheckFlag(fresh == null ? null : fresh.getDiagcheckFlag());
                // åŽŸå®žçŽ°ä¸åˆ¤ null(fresh.getLongTaskReason() + "..."),原样保留
                u.setLongTaskReason((fresh == null ? null : fresh.getLongTaskReason())
                        + "该患者所在科室未配置离院长期任务;");
                updateInhosp(u);
            } else {
                for (VisitTaskdeptRow t : taskdepts) {
                    subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 1, config);
                    generatedCount++;
                }
            }
        } catch (Exception e) {
            // åŽŸ L1879-1885:⚠️ åœ¨ã€æ–°é²œå¯¹è±¡ã€‘上 set â€”— åªå†™ deptcheck + reason
            e.printStackTrace();
            log.error("【科室任务】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            VisitInhospUpdateDto u = new VisitInhospUpdateDto();
            u.setInhospid(inhosp.getInhospid());
            u.setDeptcheckFlag("2");
            u.setLongTaskReason("科室任务处理异常");
            updateInhosp(u);
        }
        return generatedCount;
    }
    // ==========================================================================
    // 3. ç—…区维度(原 L1894-1935)
    // ==========================================================================
    /**
     * ä¸ºå•个患者生成病区维度的子任务。
     *
     * @return ç”Ÿæˆçš„子任务数量
     */
    public int addWardSubTaskForPatient(VisitInhospRow inhosp, VisitArchiveRow archive, String config) {
        int generatedCount = 0;
        try {
            String errorreason = null;
            List<VisitTaskdeptRow> taskdeptList = null;
            // åŽŸ L1899-1912
            if (!StringUtils.isEmpty(inhosp.getLeavehospitaldistrictcode())) {
                VisitTaskdeptRow query = new VisitTaskdeptRow();
                query.setLongtask(1L);
                query.setDeptCode(inhosp.getLeavehospitaldistrictcode());
                query.setDeptType(DEPT_TYPE_WARD);
                query.setServiceType(SERVICE_TYPE_DISCHARGE);
                query.setOrgid(inhosp.getOrgid());
                taskdeptList = taskdeptMapper.selectList(query);
                if (CollectionUtils.isEmpty(taskdeptList)) {
                    errorreason = "该患者所在病区未配置长期任务;";
                }
            } else {
                errorreason = "离院病区为空;";
            }
            if (StringUtils.isNotEmpty(errorreason)) {
                // åŽŸ L1914-1920:⚠️ é‡è¯»å¯¹è±¡ setWardcheck,另两个 flag åŽŸæ ·å¸¦å›ž
                VisitInhospRow fresh = inhospMapper.selectByInhospid(inhosp.getInhospid());
                VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                u.setInhospid(inhosp.getInhospid());
                u.setWardcheckFlag("2");
                u.setDeptcheckFlag(fresh == null ? null : fresh.getDeptcheckFlag());
                u.setDiagcheckFlag(fresh == null ? null : fresh.getDiagcheckFlag());
                // åŽŸæœ‰åˆ¤ç©ºï¼ˆä¸Žç§‘å®¤ç»´åº¦é‚£å¤„ä¸åŒï¼‰ï¼ŒåŽŸæ ·ä¿ç•™
                u.setLongTaskReason(StringUtils.isNotEmpty(fresh == null ? null : fresh.getLongTaskReason())
                        ? fresh.getLongTaskReason() + errorreason : errorreason);
                updateInhosp(u);
            } else {
                for (VisitTaskdeptRow t : taskdeptList) {
                    subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 2, config);
                    generatedCount++;
                }
            }
        } catch (Exception e) {
            // åŽŸ L1927-1933:新鲜对象 â€”— åªå†™ wardcheck + reason
            e.printStackTrace();
            log.error("【病区任务】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            VisitInhospUpdateDto u = new VisitInhospUpdateDto();
            u.setInhospid(inhosp.getInhospid());
            u.setWardcheckFlag("2");
            u.setLongTaskReason("病区任务处理异常");
            updateInhosp(u);
        }
        return generatedCount;
    }
    // ==========================================================================
    // 4. ç–¾ç—…维度(原 L1942-2001)
    // ==========================================================================
    /**
     * ä¸ºå•个患者生成疾病维度的子任务。
     *
     * @return ç”Ÿæˆçš„子任务数量
     */
    public int addIcd10SubTaskForPatient(VisitInhospRow inhosp, VisitArchiveRow archive, String config) {
        int generatedCount = 0;
        try {
            // åŽŸ L1945-1947
            if (StringUtils.isEmpty(inhosp.getLeaveicd10code())) {
                return 0;
            }
            // åŽŸ L1949-1963:站点差异 â€”— nhfy ç”¨è¯Šæ–­åï¼Œå…¶å®ƒç”¨ ICD10 ç¼–码
            VisitTaskdiagRow query = new VisitTaskdiagRow();
            query.setLongtask(1L);
            if (SITE_NHFY.equals(active)) {
                query.setIcd10name(inhosp.getLeavediagname());
                if (StringUtils.isEmpty(inhosp.getLeavediagname())) {
                    // åŽŸ L1954-1959:⚠️ æ–°é²œå¯¹è±¡ â€”— åªå†™ diagcheck + reason(覆盖式)
                    VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                    u.setInhospid(inhosp.getInhospid());
                    u.setDiagcheckFlag("2");
                    u.setLongTaskReason("该患者主诊断名称为空;");
                    updateInhosp(u);
                    return 0;
                }
            } else {
                query.setIcd10code(inhosp.getLeaveicd10code());
            }
            query.setOrgid(inhosp.getOrgid());
            List<VisitTaskdiagRow> taskdiags = taskdiagMapper.selectListByIcdName(query);
            if (CollectionUtils.isEmpty(taskdiags)) {
                // åŽŸ L1967-1971:⚠️ é‡è¯»å¯¹è±¡ï¼Œä½†ã€åªã€‘setDiagcheckFlag + è¦†å†™ reason
                //    ï¼ˆä¸Žç§‘室/病区那两处不同:这里不带回其它 flag)
                VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                u.setInhospid(inhosp.getInhospid());
                u.setDiagcheckFlag("2");
                u.setLongTaskReason("该患者所患疾病未配置长期任务;");
                updateInhosp(u);
            } else {
                int flag = 0;
                for (VisitTaskdiagRow t : taskdiags) {
                    // åŽŸ L1975-1983:必须科室或病区能匹配上才建
                    if (StringUtils.isNotEmpty(t.getDeptCode())
                            && t.getDeptCode().equals(inhosp.getLeaveldeptcode())) {
                        subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 3, config);
                        generatedCount++;
                        flag = 1;
                    } else if (StringUtils.isNotEmpty(t.getWardCode())
                            && t.getWardCode().equals(inhosp.getLeavehospitaldistrictcode())) {
                        subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 3, config);
                        generatedCount++;
                        flag = 1;
                    }
                }
                if (flag == 0) {
                    // åŽŸ L1985-1991
                    log.error("该科室或病区对于患者所患疾病未配置长期任务,患者:{}", inhosp.getInhospid());
                    VisitInhospUpdateDto u = new VisitInhospUpdateDto();
                    u.setInhospid(inhosp.getInhospid());
                    u.setDiagcheckFlag("2");
                    u.setLongTaskReason("该科室或病区对于患者所患疾病未配置长期任务;");
                    updateInhosp(u);
                }
            }
        } catch (Exception e) {
            // åŽŸ L1993-1998:新鲜对象 â€”— åªå†™ diagcheck + reason
            e.printStackTrace();
            log.error("【疾病任务】患者{}处理异常:{}", inhosp.getInhospid(), e.getMessage());
            VisitInhospUpdateDto u = new VisitInhospUpdateDto();
            u.setInhospid(inhosp.getInhospid());
            u.setDiagcheckFlag("2");
            u.setLongTaskReason("疾病任务处理异常");
            updateInhosp(u);
        }
        return generatedCount;
    }
    // ==========================================================================
    // 5. å†å…¥é™¢å¤„理(原 L1321-1359)
    // ==========================================================================
    /**
     * å†å…¥é™¢å¤„理:新入院记录出现时,把该患者<b>旧的待执行随访</b>停掉。
     * <p>
     * é€è¡Œå¯¹åº”原 {@code dealInHospInfo(List<PatMedInhosp> patMedInhospList)}。
     * <p>
     * âš ï¸ åŽŸå®žçŽ°çš„ä¸¤ä¸ªè¦ç‚¹ï¼š
     * <ol>
     *   <li>查询条件是 {@code patid + sendstate=2},<b>不设 taskid</b>(按患者全量查);
     *       è¿™é‡Œä¼  {@code null} è¡¨ç¤ºä¸æŒ‰ taskid è¿‡æ»¤ï¼›</li>
     *   <li>景宁({@code 47255004333112711A1001})跳过停机 â€”— ä¸è¿‡è¿™æ¬¡åˆ¤çš„æ˜¯
     *       <b>{@code serviceSubtask1.getOrgid()}</b>(子任务的 orgid),不是入参患者的 orgid。</li>
     * </ol>
     *
     * @param newInHosp æ–°å…¥é™¢çš„出入院记录(用它和旧子任务的 endtime åšæ¯”较)
     */
    public void dealInHospInfo(VisitInhospRow newInHosp) {
        try {
            VisitTaskCreateApi api = taskApi();
            if (api == null) {
                log.warn("【出入院随访】VisitTaskCreateApi ä¸å¯ç”¨ï¼Œè·³è¿‡å†å…¥é™¢å¤„理。inhospid={}",
                        newInHosp.getInhospid());
                return;
            }
            // åŽŸ L1324-1328:patid + sendstate=2(不设 taskid)
            List<VisitPendingSubtaskDto> pendingList =
                    api.findPendingSubtasks(newInHosp.getPatid(), null);
            if (CollectionUtils.isEmpty(pendingList)) {
                return;
            }
            for (VisitPendingSubtaskDto pending : pendingList) {
                // åŽŸ L1332-1335:不是长期任务,不处理
                if (pending.getLongSendTime() == null) {
                    continue;
                }
                // åŽŸ L1336-1340:⚠️ åˆ¤çš„æ˜¯ã€å­ä»»åŠ¡çš„ orgid】
                boolean firstVisit = ORG_JINGNING.equals(pending.getOrgid());
                if (firstVisit) {
                    continue;
                }
                // åŽŸ L1342-1350:旧子任务的出院时间在新入院时间之前 â†’ åœæŽ‰
                if (pending.getEndtime() != null && newInHosp.getStarttime() != null
                        && pending.getEndtime().before(newInHosp.getStarttime())) {
                    pending.setSendstate(4L);
                    pending.setIsVisitAgain(2);
                    pending.setRemark("患者再入院");
                    pending.setResult("error");
                    api.updatePendingSubtask(pending);
                }
            }
        } catch (Exception e) {
            // åŽŸ L1355-1357
            log.error("入院数据信息出异常了:{},{}", newInHosp, e.getMessage());
        }
    }
    /** æ™¯å®æœºæž„ID(原 L1338;与 InhospSubtaskWriter é‡Œé‚£ä»½æ˜¯åŒä¸€å¸¸é‡ï¼Œå·²é€å­—核对) */
    private static final String ORG_JINGNING = "47255004333112711A1001";
    // ==========================================================================
    // è¾…助
    // ==========================================================================
    private void updateInhosp(VisitInhospUpdateDto u) {
        PublicityDataPort port = dataPort();
        if (port == null) {
            log.warn("【出入院随访】PublicityDataPort ä¸å¯ç”¨ï¼Œæ— æ³•写回标志位。inhospid={}", u.getInhospid());
            return;
        }
        port.updateVisitInhosp(u);
    }
    private PublicityDataPort dataPort() {
        if (dataPortProvider == null) {
            return null;
        }
        try {
            return dataPortProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 PublicityDataPort å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    private VisitTaskCreateApi taskApi() {
        if (taskCreateApiProvider == null) {
            return null;
        }
        try {
            return taskCreateApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 VisitTaskCreateApi å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    /**
     * {@code service_task_rule} çš„规则快照(原 {@code ServiceTaskRule} çš„随访侧视图)。
     * <p>
     * è§„则查询本身仍由 {@code PublicityDataPort} æä¾›ï¼ˆå®ƒå·²ç»åœ¨æœåŠ¡å®£æ•™é“¾è·¯ï¼‰ï¼Œ
     * æœ¬ç±»åªæ¶ˆè´¹ç»“果,避免为规则表再加一个 Mapper。
     */
    public static class VisitTaskRule {
        /** ç§‘室规则:"1"=首要 "2"=次要,其它=不参与 */
        private String deptRule;
        /** ç—…区规则(原字段名就是拼错的 {@code wradRule},保持同名以免读错) */
        private String wradRule;
        /** ç–¾ç—…规则 */
        private String diagRule;
        public String getDeptRule() {
            return deptRule;
        }
        public void setDeptRule(String deptRule) {
            this.deptRule = deptRule;
        }
        public String getWradRule() {
            return wradRule;
        }
        public void setWradRule(String wradRule) {
            this.wradRule = wradRule;
        }
        public String getDiagRule() {
            return diagRule;
        }
        public void setDiagRule(String diagRule) {
            this.diagRule = diagRule;
        }
    }
    /** ä¾›æµ‹è¯•/日志:本类是否拿到了必需的 Mapper */
    boolean hasMappers() {
        return inhospMapper != null && archiveMapper != null
                && taskdeptMapper != null && taskdiagMapper != null;
    }
    /** é¢„留:供日志使用的时间戳(避免 IDE æŠ¥æœªä½¿ç”¨ import Date) */
    @SuppressWarnings("unused")
    private Date now() {
        return new Date();
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/InhospVisitTaskService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,329 @@
package com.ruoyi.inhospvisit.service;
import com.ruoyi.common.core.service.OtherVisitApi;
import com.ruoyi.common.core.service.PublicityApi;
import com.ruoyi.common.core.service.PublicityDataPort;
import com.ruoyi.common.core.service.dto.InHospPublicityRowDto;
import com.ruoyi.common.core.service.dto.VisitTaskRuleDto;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.mapper.VisitArchiveMapper;
import com.ruoyi.inhospvisit.mapper.VisitInhospMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
 * å‡ºå…¥é™¢éšè®¿çš„<b>两个入口</b> â€”— é€è¡Œæ¬è‡ª {@code PatMedInhospServiceImpl} çš„
 * {@code dealOutHospInfo}(L324-463,140 è¡Œï¼‰ä¸Ž {@code dealOutHospInfoBySchedule}(L471-614,144 è¡Œï¼‰ã€‚
 *
 * <h3>⚠️ è¿™ä¸€æ­¥æ˜¯"切换调用点",必须在同一步完成三件事</h3>
 * <ol>
 *   <li><b>宣教改走 {@link PublicityApi}</b>:原 L331-409(入院宣教)与 L411-444(药品宣教)
 *       æœ¬æ¬¡<b>不再内联</b>,改为回调宣教模块。若不同时删掉旧的内联块,
 *       <b>宣教会发两遍</b>(第 5 æ­¥å°±ç•™ä¸‹çš„已知中间状态);</li>
 *   <li><b>手术随访改走 {@link OtherVisitApi}</b>:原 L449-451 ä¸Ž L559-608 é‚£ä¸¤å¤„ï¼›</li>
 *   <li><b>编排层改走本模块的 {@link InhospTaskOrchestrator}</b>:原 {@code addSubTaskByRule} /
 *       {@code dealInHospInfo}。</li>
 * </ol>
 *
 * <h3>与原实现的两处结构差异(都已登记)</h3>
 * <ol>
 *   <li><b>患者列表的取数方式变了</b>:原实现直接查 {@code pat_med_inhosp};
 *       æ¬è¿åŽèµ°æœ¬æ¨¡å—自带的 {@link VisitInhospMapper}(SQL ä¸Ž smartor é‚£ä»½é€æ¡ç­‰ä»·ï¼Œ
 *       ç”± {@code verify_inhospvisit_sql_expansion.py} æŠŠå…³ï¼‰ã€‚
 *       æ³¨æ„åŽŸå®žçŽ°ç¬¬ä¸€æ®µæŸ¥çš„æ˜¯ {@code deptcheckFlag="0" + inhospstate="1" + fuflag="1"},
 *       è¿™æ®µæ—¢ç”¨äºŽå®£æ•™ä¹Ÿç”¨äºŽ `addSubTaskByRule`。</li>
 *   <li><b>宣教的患者列表由宣教模块自己查</b>:原实现复用同一个 {@code patMedInhosps} åˆ—表;
 *       æ¬è¿åŽ {@link PublicityApi#dealInHospPublicity} å†…部自己查一份(条件相同)。
 *       è¿™æ˜¯<b>故意的</b>:宣教模块不该依赖随访模块的内存列表。
 *       ä»£ä»·æ˜¯å¤šä¸€æ¬¡è½»é‡æŸ¥è¯¢ï¼Œå·²åœ¨ {@code PublicityServiceImpl} çš„æ³¨é‡Šé‡Œè¯´æ˜Žã€‚</li>
 * </ol>
 */
@Slf4j
@Service
public class InhospVisitTaskService {
    @Value("${spring.profiles.active}")
    private String active;
    @Autowired
    private VisitInhospMapper inhospMapper;
    @Autowired
    private VisitArchiveMapper archiveMapper;
    @Autowired
    private InhospTaskOrchestrator orchestrator;
    @Autowired
    private ObjectProvider<PublicityApi> publicityApiProvider;
    @Autowired
    private ObjectProvider<OtherVisitApi> otherVisitApiProvider;
    @Autowired
    private ObjectProvider<PublicityDataPort> dataPortProvider;
    // ==========================================================================
    // å…¥å£ 1:出院随访主扫描(原 L324-463)
    // ==========================================================================
    /**
     * å‡ºé™¢éšè®¿ä¸»æ‰«æã€‚顺序<b>逐条对应</b>原实现:
     * <ol>
     *   <li>取"科室维度未处理"的在院患者(原 L325-329);</li>
     *   <li>入院宣教 â†’ å›žè°ƒ {@link PublicityApi#dealInHospPublicity}(原 L331-409);</li>
     *   <li>药品宣教(仅 sltd)→ å›žè°ƒ {@link PublicityApi#dealMedicalPublicity}(原 L411-444);</li>
     *   <li>基于规则的出院随访 â†’ {@link #addSubTaskByRule}(原 L448);</li>
     *   <li>手术随访(非 xh)→ å›žè°ƒ {@link OtherVisitApi#addOperationSubTask}(原 L449-451);</li>
     *   <li>再入院处理 â†’ {@link #dealInHospInfo}(原 L454-461)。</li>
     * </ol>
     *
     * @param config {@code sys_config: visit.early.day}
     * @return åŽŸå®žçŽ°å›ºå®šè¿”å›ž 1
     */
    public int dealOutHospInfo(String config) {
        // åŽŸ L325-329:科室维度未处理的在院患者
        VisitInhospRow pmks = new VisitInhospRow();
        pmks.setDeptcheckFlag("0");
        pmks.setInhospstate("1");
        pmks.setFuflag("1");
        List<VisitInhospRow> patMedInhosps = inhospMapper.selectForScan(pmks);
        // åŽŸ L331-409 + L411-444:两段宣教改为回调宣教模块
        // âš ï¸ è¿™é‡Œä¸å†å†…联实现 â€”— å¦åˆ™ä¼šä¸Ž ruoyi-publicity é‡Œçš„实现"发两遍"
        dealInHospPublicityAndMedical(config);
        // åŽŸ L445-451:基于规则的出院随访 + æ‰‹æœ¯éšè®¿
        addSubTaskByRule(config, patMedInhosps);
        if (!"xh".equals(active)) {
            // åŽŸ L450:addOperationSubTask(config)
            callOperationVisit(config);
        }
        // åŽŸ L454-461:再入院处理
        VisitInhospRow inHospQuery = new VisitInhospRow();
        inHospQuery.setDeptcheckFlag("0");
        inHospQuery.setInhospstate("0");
        List<VisitInhospRow> inHospList = inhospMapper.selectForScan(inHospQuery);
        if (CollectionUtils.isNotEmpty(inHospList)) {
            dealInHospInfo(inHospList);
        }
        return 1;
    }
    /**
     * ä¸¤æ®µå®£æ•™å›žè°ƒï¼ˆåŽŸ L331-409 å…¥é™¢å®£æ•™ + L411-444 è¯å“å®£æ•™ï¼‰ã€‚
     * <p>
     * é¡ºåºä¸ŽåŽŸå®žçŽ°ä¸€è‡´ï¼š<b>先入院宣教,再药品宣教</b>。
     * å–不到宣教模块时按"没有宣教要做"处理并打 WARN(fail-open)。
     */
    private void dealInHospPublicityAndMedical(String config) {
        PublicityApi api = publicityApi();
        if (api == null) {
            log.warn("【出入院随访】PublicityApi ä¸å¯ç”¨ï¼ˆå®£æ•™æ¨¡å—未装配),跳过入院宣教与药品宣教。");
            return;
        }
        try {
            // åŽŸ L331-409
            api.dealInHospPublicity(config);
        } catch (Exception e) {
            // å®£æ•™å¤±è´¥ä¸èƒ½æ‹–垮随访主流程
            log.warn("【出入院随访】入院宣教回调失败,已忽略:{}", e.toString());
        }
        try {
            // åŽŸ L411-444
            api.dealMedicalPublicity(config);
        } catch (Exception e) {
            log.warn("【出入院随访】药品宣教回调失败,已忽略:{}", e.toString());
        }
    }
    /**
     * æ‰‹æœ¯éšè®¿å›žè°ƒï¼ˆåŽŸ L449-451 ä¸Ž L559-608 çš„ {@code addOperationSubTask})。
     */
    private void callOperationVisit(String config) {
        OtherVisitApi api = otherVisitApi();
        if (api == null) {
            log.warn("【出入院随访】OtherVisitApi ä¸å¯ç”¨ï¼ˆæ‰‹æœ¯éšè®¿æ¨¡å—未装配),跳过手术随访。");
            return;
        }
        try {
            api.addOperationSubTask(config);
        } catch (Exception e) {
            log.warn("【出入院随访】手术随访回调失败,已忽略:{}", e.toString());
        }
    }
    /**
     * é€æ‚£è€…做规则编排(原 L448 çš„ {@code addSubTaskByRule(config, patMedInhosps)})。
     * <p>
     * åŽŸ {@code addSubTaskByRule}(L1691) æ˜¯ä¸ª"遍历患者"的外层方法,
     * æœ¬æ¬¡æŠŠ"遍历"放在这里,把"单患者规则判定"放进
     * {@link InhospTaskOrchestrator#addSubTaskByRule}。
     * è¿™æ ·åˆ‡åˆ†ä¹‹åŽï¼Œä¸¤è¾¹çš„æ–¹æ³•名相同但职责不同,已在各自注释里写明。
     */
    private void addSubTaskByRule(String config, List<VisitInhospRow> patMedInhosps) {
        if (CollectionUtils.isEmpty(patMedInhosps)) {
            return;
        }
        for (VisitInhospRow row : patMedInhosps) {
            // åŽŸ L1692-1700:获取患者基本信息,为空则写 deptcheck=2 + reason
            VisitArchiveRow archive = archiveMapper.selectByPatid(row.getPatid());
            if (archive == null) {
                com.ruoyi.common.core.service.dto.VisitInhospUpdateDto u =
                        new com.ruoyi.common.core.service.dto.VisitInhospUpdateDto();
                u.setInhospid(row.getInhospid());
                u.setDeptcheckFlag("2");
                u.setLongTaskReason("患者基本信息为空");
                updateInhosp(u);
                continue;
            }
            try {
                // åŽŸ L1703-1707:按离院科室/病区查规则
                VisitTaskRuleDto rule = findTaskRule(row);
                // åŽŸ L1709 èµ·ï¼šäº¤ç»™ç¼–排器按规则执行
                orchestrator.addSubTaskByRule(row, archive, toOrchestratorRule(rule), config);
            } catch (Exception e) {
                // åŽŸ L1839-1845(在编排器里也有,但外层遍历这一层原实现没有 try/catch;
                // è¿™é‡ŒåŠ ä¸€å±‚æ˜¯ä¸ºäº†"单患者失败不影响其它患者",属健壮性改进,已登记)
                log.error("【基于规则】患者{}外层处理异常:{}", row.getInhospid(), e.getMessage());
                com.ruoyi.common.core.service.dto.VisitInhospUpdateDto u =
                        new com.ruoyi.common.core.service.dto.VisitInhospUpdateDto();
                u.setInhospid(row.getInhospid());
                u.setDeptcheckFlag("2");
                u.setLongTaskReason("基于规则处理异常");
                updateInhosp(u);
            }
        }
    }
    /**
     * å†å…¥é™¢å¤„理(原 L454-461 çš„ {@code dealInHospInfo(patMedInhospList)})。
     */
    private void dealInHospInfo(List<VisitInhospRow> inHospList) {
        for (VisitInhospRow row : inHospList) {
            orchestrator.dealInHospInfo(row);
        }
    }
    // ==========================================================================
    // å…¥å£ 2:任务组首轮纳入(原 L471-614)
    // ==========================================================================
    /**
     * ä»»åŠ¡ç»„ï¼ˆappltype=5)首轮纳入 â€”— <b>本次不搬,仍留在 smartor</b>。
     *
     * <h3>为什么不搬(一个必须说明的范围决定)</h3>
     * åŽŸ {@code dealOutHospInfoBySchedule}(L471-614) ç”±ä¸¤æ®µæž„成:
     * <ul>
     *   <li><b>非手术分支</b>(L518-558):按维度过滤扫患者 â†’ {@code boxedServiceSubtask} â†’
     *       {@code scheduleSubtaskBuilder.buildSubtasksForPatient}(一次生成 N æ¡ï¼‰â†’ ç½® flag;</li>
     *   <li><b>手术分支</b>(L559-608):{@code selectOperationItemListMaxLevel} +
     *       {@code boxedServiceSubtaskForOp} â†’ åŒä¸€ builder。</li>
     * </ul>
     * ä¸¤æ®µéƒ½ä¾èµ– {@code ScheduleSubtaskBuilder} â€”— å®ƒåŒæ—¶æœåŠ¡"HIS è‡ªåŠ¨çº³å…¥"与
     * "前端手动勾选患者"两条路径(见其类注释),**不属于本次范围**。
     * è€Œæ‰‹æœ¯åˆ†æ”¯æŒ‰ç‰ˆå›¾å±žç¬¬äºŒæ‰¹çš„ {@code ruoyi-othervisit}。
     * <p>
     * æ‰€ä»¥æœ¬æ¬¡æŠŠ<b>整个方法留在 smartor</b>(行为一行不变),
     * ç­‰ç¬¬äºŒæ‰¹åš {@code ruoyi-othervisit} æ—¶è¿žåŒä»»åŠ¡ç»„é“¾è·¯ä¸€èµ·æ”¶èµ°ã€‚
     * <p>
     * è¿™æ ·åšçš„好处:本轮不需要为 {@code ScheduleSubtaskBuilder} å†é€ ä¸€ä¸ªç«¯å£ï¼Œ
     * ä¹Ÿä¸ä¼šå› ä¸º"搬一半留一半"而在两处生成任务组的子任务。
     *
     * @param config {@code sys_config: visit.early.day}
     * @return åŽŸå®žçŽ°å›ºå®šè¿”å›ž 1
     */
    public int dealOutHospInfoBySchedule(String config) {
        log.warn("【出入院随访】dealOutHospInfoBySchedule æœ¬æ¬¡æœªæ¬è¿ï¼ˆå«æ‰‹æœ¯åˆ†æ”¯ä¸Žä»»åŠ¡ç»„ builder,"
                + "属第二批 ruoyi-othervisit èŒƒå›´ï¼‰ï¼Œä»ç”± smartor çš„实现承接。config={}", config);
        return 1;
    }
    // ==========================================================================
    // è¾…助
    // ==========================================================================
    /** åŽŸ L1703-1707:按离院科室/病区查规则(取不到端口时返回 null = æœªé…ç½®è§„则 â†’ å…¨éƒ¨ç”Ÿæˆï¼‰ */
    private VisitTaskRuleDto findTaskRule(VisitInhospRow row) {
        PublicityDataPort port = dataPort();
        if (port == null) {
            log.warn("【出入院随访】PublicityDataPort ä¸å¯ç”¨ï¼Œè§„则按「未配置」处理(全部生成)。inhospid={}",
                    row.getInhospid());
            return null;
        }
        try {
            return port.findTaskRule(row.getLeaveldeptcode(), row.getLeavehospitaldistrictcode());
        } catch (Exception e) {
            log.warn("【出入院随访】查任务规则失败,按「未配置」处理:{}", e.toString());
            return null;
        }
    }
    /** æŠŠç«¯å£çš„规则 DTO é€‚配成编排器内嵌的规则视图(字段一一对应,含拼错的 {@code wradRule}) */
    private InhospTaskOrchestrator.VisitTaskRule toOrchestratorRule(VisitTaskRuleDto dto) {
        if (dto == null) {
            return null;
        }
        InhospTaskOrchestrator.VisitTaskRule r = new InhospTaskOrchestrator.VisitTaskRule();
        r.setDeptRule(dto.getDeptRule());
        r.setWradRule(dto.getWradRule());
        r.setDiagRule(dto.getDiagRule());
        return r;
    }
    private void updateInhosp(com.ruoyi.common.core.service.dto.VisitInhospUpdateDto u) {
        PublicityDataPort port = dataPort();
        if (port == null) {
            log.warn("【出入院随访】PublicityDataPort ä¸å¯ç”¨ï¼Œæ— æ³•写回标志位。inhospid={}", u.getInhospid());
            return;
        }
        port.updateVisitInhosp(u);
    }
    private PublicityDataPort dataPort() {
        if (dataPortProvider == null) {
            return null;
        }
        try {
            return dataPortProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 PublicityDataPort å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    private PublicityApi publicityApi() {
        if (publicityApiProvider == null) {
            return null;
        }
        try {
            return publicityApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 PublicityApi å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    private OtherVisitApi otherVisitApi() {
        if (otherVisitApiProvider == null) {
            return null;
        }
        try {
            return otherVisitApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 OtherVisitApi å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/TaskGroupCycleRoller.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
package com.ruoyi.inhospvisit.service;
import com.ruoyi.common.core.service.ScheduleRollPort;
import com.ruoyi.common.core.service.dto.VisitScheduleRollResultDto;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * ä»»åŠ¡ç»„ã€Œè½®æ¬¡æ»šåŠ¨ã€ç¼–æŽ’å™¨ â€”— é€è¡Œæ¬è‡ª
 * {@code ServiceTaskScheduleServiceImpl#rollCycleTasks}(L170-190,21 è¡Œï¼‰ã€‚
 *
 * <h3>为什么这里只有"编排"(第 9 æ­¥çš„设计)</h3>
 * çœŸæ­£æ»šåŠ¨å•ä¸ªè®¡åˆ’è¦åšä¸€æ¬¡ {@code ServiceSubtask} çš„ <b>116 å­—段全字段深拷贝</b>,
 * é‚£éƒ¨åˆ†<b>刻意留在实现侧</b>(smartor çš„ {@code ScheduleRollPortBridge}),
 * ä¸è·¨è¶Šæ¨¡å—边界传 116 å­—段的 DTO â€”— ç†ç”±è§ {@link ScheduleRollPort} çš„类注释。
 * <p>
 * æœ¬ç±»è´Ÿè´£çš„部分(与原文逐行对应):
 * <ol>
 *   <li>批次上限 {@code ROLL_BATCH_LIMIT = 200}(原 L50 / L171);</li>
 *   <li>扫描日志 {@code [任务组-滚动] æ‰«æåˆ° {} ä¸ªå¾…评估的循环计划}(原 L175);</li>
 *   <li>逐条 {@code try/catch},单条失败不影响其它计划(原 L178-186);</li>
 *   <li>计数与收尾日志 {@code æœ¬æ¬¡æŽ¨è¿› {} ä¸ªè®¡åˆ’}(原 L188)。</li>
 * </ol>
 *
 * <h3>⚠️ ä¸ŽåŽŸå®žçŽ°çš„ä¸¤å¤„å·²çŸ¥å·®å¼‚ï¼ˆéƒ½å·²ç™»è®°ï¼‰</h3>
 * <ol>
 *   <li><b>事务边界</b>:原实现在 {@code rollOneSchedule} ä¸Šæ ‡ {@code @Transactional}
 *       ï¼ˆæ–¹æ³•在同一个类里,通过 {@code SpringUtils.getAopProxy(this)} è‡ªè°ƒä»¥è®©æ³¨è§£ç”Ÿæ•ˆï¼Œè§åŽŸ L180)。
 *       æ¬è¿åŽ {@code @Transactional} è½åœ¨æ¡¥å®žçް {@code ScheduleRollPortBridge#rollOneSchedule} ä¸Š
 *       â€”— <b>è·¨ Bean è°ƒç”¨ï¼Œäº‹åŠ¡ç…§å¸¸ç”Ÿæ•ˆ</b>,所以不再需要 AOP è‡ªè°ƒé‚£ä¸€æ­¥ã€‚
 *       è¿™æ˜¯ç­‰ä»·æ”¹å†™ï¼Œä¸æ˜¯è¡Œä¸ºå˜æ›´ã€‚</li>
 *   <li><b>计划对象是重查的</b>:原实现把"查询结果里的计划对象"直接传给 {@code rollOneSchedule};
 *       ç«¯å£åªç»™ id,所以桥里按 id é‡æŸ¥ä¸€æ¬¡ã€‚差异只在"两次读取之间计划是否被改过"这个毫秒级窗口,
 *       ä¸” roll å†…部仍会做"本轮是否结束"的校验。已在桥的注释里说明。</li>
 * </ol>
 *
 * <h3>⚠️ è¿‡æ¸¡æœŸç¦æ­¢åŒè·‘</h3>
 * æœ¬ç±»åœ¨ç¬¬ 10 æ­¥ï¼ˆæŠŠ {@code RyTask.rollTaskGroupCycle} çš„æ–¹æ³•体切过来)之前,
 * <b>不能被挂到任何调度上</b>:老的 {@code rollCycleTasks} è¿˜åœ¨è·‘,
 * ä¸¤è¾¹éƒ½ä¼š"推进轮次 + æ–°å»º subtask",双跑会造成重复轮次。
 * ç›®å‰æœ¬ç±»åªè¢«è‡ªæ£€ç¨‹åºè°ƒç”¨ã€‚
 */
@Slf4j
@Service
public class TaskGroupCycleRoller {
    /** å•次扫描的最大计划数,避免长事务(原 {@code ServiceTaskScheduleServiceImpl} L50)。 */
    private static final int ROLL_BATCH_LIMIT = 200;
    @Autowired
    private ObjectProvider<ScheduleRollPort> rollPortProvider;
    /**
     * æ‰«æå¾ªçŽ¯ä»»åŠ¡ç»„è®¡åˆ’å¹¶æŽ¨è¿›å·²å®Œæˆçš„è½®æ¬¡ã€‚
     * <p>
     * é€è¡Œå¯¹åº”原 {@code rollCycleTasks()}(L170-190)。
     *
     * @return æœ¬æ¬¡æŽ¨è¿›çš„计划数
     */
    public int rollCycleTasks() {
        ScheduleRollPort port = rollPort();
        if (port == null) {
            log.warn("【出入院随访】ScheduleRollPort ä¸å¯ç”¨ï¼ˆä»»åŠ¡ç»„ç«¯å£æœªè£…é…ï¼‰ï¼Œæœ¬æ¬¡ä¸æŽ¨è¿›ä»»ä½•è½®æ¬¡ã€‚");
            return 0;
        }
        // åŽŸ L171-174
        List<Long> scheduleIds = port.selectActiveCycleSchedules(ROLL_BATCH_LIMIT);
        if (CollectionUtils.isEmpty(scheduleIds)) {
            return 0;
        }
        // åŽŸ L175
        log.info("[任务组-滚动] æ‰«æåˆ° {} ä¸ªå¾…评估的循环计划", scheduleIds.size());
        int rolled = 0;
        for (Long scheduleId : scheduleIds) {
            try {
                // åŽŸ L180:if (SpringUtils.getAopProxy(this).rollOneSchedule(schedule)) rolled++;
                // æ¬è¿åŽ @Transactional åœ¨æ¡¥å®žçŽ°ä¸Šï¼Œè·¨ Bean è°ƒç”¨å³ç”Ÿæ•ˆï¼Œæ— éœ€ AOP è‡ªè°ƒã€‚
                VisitScheduleRollResultDto result = port.rollOneSchedule(scheduleId);
                if (result != null && result.isRolled()) {
                    rolled++;
                }
            } catch (Exception e) {
                // åŽŸ L183-186:单条失败不影响其它计划,记日志继续
                log.error("[任务组-滚动] scheduleId={} æ»šåŠ¨å¤±è´¥ï¼š{}", scheduleId, e.getMessage(), e);
            }
        }
        // åŽŸ L188-189
        log.info("[任务组-滚动] æœ¬æ¬¡æŽ¨è¿› {} ä¸ªè®¡åˆ’", rolled);
        return rolled;
    }
    private ScheduleRollPort rollPort() {
        if (rollPortProvider == null) {
            return null;
        }
        try {
            return rollPortProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 ScheduleRollPort å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/service/VisitPreachformWriter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,142 @@
package com.ruoyi.inhospvisit.service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.core.service.VisitTaskCreateApi;
import com.ruoyi.common.core.service.dto.VisitSubtaskDraft;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitPreachformRow;
import com.ruoyi.inhospvisit.mapper.VisitPreachformMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Objects;
/**
 * ã€Œæ‰§è¡Œæ–¹å¼æ˜Žç»†ã€å†™å…¥å™¨ â€”— é€è¡Œæ¬è‡ª {@code PatMedInhospServiceImpl#addServiceSubtaskPreachform}
 * ï¼ˆL1061-1103,43 è¡Œï¼‰ï¼Œå¹¶å®žçŽ°ç¬¬ 7 æ­¥é¢„留的
 * {@link InhospSubtaskWriter.SubtaskPreachformWriter} æŽ¥å£ã€‚
 *
 * <h3>行为逐条对照</h3>
 * <ol>
 *   <li>按 taskid æŸ¥ä»»åŠ¡ï¼›æŸ¥ä¸åˆ° â†’ æ‰“ ERROR å¹¶ return 0(<b>不抛异常</b>);</li>
 *   <li>取 {@code serviceTask.preachformDesc};为空则整块跳过(返回 0);</li>
 *   <li>用 Jackson æŠŠ JSON ååºåˆ—化成 {@code List<VisitPreachformRow>};
 *       <b>解析失败只打 ERROR,不中断</b>(随后 list ä¸º null â†’ èµ°"解析后为空"分支);</li>
 *   <li>解析结果为空 â†’ æ‰“ ERROR å¹¶ return 0;</li>
 *   <li>逐条:设 {@code taskid} â†’ {@code subid};
 *       <b>先查重</b>(已存在则 continue,不新增);
 *       {@code sendstate} é»˜è®¤ "1",调用方传了非空值则覆写;
 *       è®¾ {@code orgid};insert。</li>
 * </ol>
 *
 * <h3>⚠️ åŽŸå®žçŽ°çš„ 3 ä¸ªè¦ç‚¹ï¼ŒåŽŸæ ·ä¿ç•™</h3>
 * <ul>
 *   <li>返回值【永远是 0】(原 L1102 {@code return 0;}),调用方也没有用它的返回值
 *       â€”— æ‰€ä»¥æœ¬ç±»ä¹Ÿè¿”回 0,不做"顺手改成成功条数";</li>
 *   <li>查重用的条件是 {@code ServiceSubtaskPreachform(subid, taskid, preachform)}
 *       â€”— <b>注意没有设 orgid</b>(虽然 XML æ”¯æŒè¯¥æ¡ä»¶ï¼‰ã€‚原样保留;</li>
 *   <li>解析失败时 {@code serviceSubtaskPreachformList} ä¿æŒ null,
 *       ç”± {@code CollectionUtils.isEmpty(null) == true} èµ°è¿›"解析后为空"分支 â€”— åŽŸæ ·ä¿ç•™è¿™ä¸ªæŽ§åˆ¶æµã€‚</li>
 * </ul>
 *
 * <h3>⚠️ ä¸Žè°ƒç”¨æ–¹çš„事务关系(一处已登记的差异)</h3>
 * åŽŸå®žçŽ°é‡Œ {@code addServiceSubtaskPreachform} ä¸Ž {@code insertServiceSubtask} åœ¨<b>同一个 try</b> å†…,
 * å®ƒæŠ›å¼‚常会被 {@code writeInSubTask} çš„ catch æ•获并把标志位置 "2"。
 * æ¬è¿åŽ {@link InhospSubtaskWriter} æŠŠå®ƒæ”¾åœ¨è‡ªå·±çš„ try é‡ŒæŒ‰"尽力而为"处理
 * ï¼ˆè§è¯¥æ–¹æ³•注释)。这是<b>有意的健壮性改进</b>:写执行方式明细失败不该
 * è®©æ•´æ¡éšè®¿å­ä»»åŠ¡è¢«åˆ¤ä¸ºç”Ÿæˆå¤±è´¥ã€‚å·²ç™»è®°åœ¨ {@code verify_inhospvisit_writein_semantics.py} çš„说明里。
 */
@Slf4j
@Service
public class VisitPreachformWriter implements InhospSubtaskWriter.SubtaskPreachformWriter {
    /** åŽŸå®žçŽ°é‡Œæ³¨é‡Šå†™æ˜Žã€Œè¿™ä¸ª sendstate éœ€è¦åœ¨æ•°æ®åº“设置一个默认值为"1"」 */
    private static final String DEFAULT_SENDSTATE = "1";
    @Autowired
    private VisitPreachformMapper preachformMapper;
    @Autowired
    private ObjectProvider<VisitTaskCreateApi> taskCreateApiProvider;
    private final ObjectMapper objectMapper = new ObjectMapper();
    @Override
    public void addServiceSubtaskPreachform(VisitSubtaskDraft serviceSubtask, String sendstate) {
        // åŽŸ L1063-1067:按 taskid æŸ¥ä»»åŠ¡ï¼ˆè¿™é‡Œèµ°ä»»åŠ¡åˆ›å»ºåŸŸçš„æŽ¥å£ï¼Œè€Œä¸æ˜¯ç›´æŽ¥æŸ¥ service_task)
        VisitTaskCreateApi api = taskApi();
        String preachformDesc = api == null ? null : api.findPreachformDesc(serviceSubtask.getTaskid());
        if (api == null) {
            log.error("addServiceSubtaskPreachform taskid={} æŸ¥ä¸åˆ°ä»»åŠ¡ï¼Œè·³è¿‡preachform子表生成",
                    serviceSubtask.getTaskid());
            return;
        }
        // åŽŸ L1070-1071
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(preachformDesc)) {
            // åŽŸ L1072-1078:反序列化;失败只打 ERROR,list ä¿æŒ null
            List<VisitPreachformRow> list = null;
            try {
                list = objectMapper.readValue(preachformDesc, new TypeReference<List<VisitPreachformRow>>() {
                });
            } catch (Exception e) {
                log.error("preachform转List<ServiceSubtaskPreachform>报错了:{}", e.getMessage());
            }
            // åŽŸ L1079-1083
            if (CollectionUtils.isEmpty(list)) {
                log.error("preachformDesc解析后为空,跳过preachform子表生成,taskid为:{},preachformDesc为:{}",
                        serviceSubtask.getTaskid(), preachformDesc);
                return;
            }
            // åŽŸ L1084-1098
            for (VisitPreachformRow row : list) {
                row.setTaskid(serviceSubtask.getTaskid());
                row.setSubid(serviceSubtask.getId());
                // åŽŸ L1088-1090:先查重,已存在就不新增
                VisitPreachformRow query = new VisitPreachformRow();
                query.setSubid(row.getSubid());
                query.setTaskid(row.getTaskid());
                query.setPreachform(row.getPreachform());
                List<VisitPreachformRow> exists = preachformMapper.selectList(query);
                if (CollectionUtils.isNotEmpty(exists)) {
                    continue;
                }
                // åŽŸ L1092-1094:默认 "1",调用方给了非空值则覆写
                row.setSendstate(DEFAULT_SENDSTATE);
                if (StringUtils.isNotEmpty(sendstate)) {
                    row.setSendstate(sendstate);
                }
                // åŽŸ L1096-1097
                row.setOrgid(serviceSubtask.getOrgid());
                preachformMapper.insert(row);
            }
        }
        // åŽŸ L1102:返回值固定为 0(见类注释)
    }
    private VisitTaskCreateApi taskApi() {
        if (taskCreateApiProvider == null) {
            return null;
        }
        try {
            return taskCreateApiProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【出入院随访】获取 VisitTaskCreateApi å¤±è´¥ï¼š{}", e.toString());
            return null;
        }
    }
    /** ä¾›æµ‹è¯•断言:确认本类拿到的 ObjectMapper éžç©ºï¼ˆåŽŸå®žçŽ°æ˜¯æ–¹æ³•å†… new,这里提为字段) */
    boolean hasObjectMapper() {
        return Objects.nonNull(objectMapper);
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/support/InhospFlagWriter.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,208 @@
package com.ruoyi.inhospvisit.support;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.mapper.VisitInhospMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
 * éšè®¿æ ‡å¿—位回写器 â€”— ç»Ÿä¸€å°è£…"改 {@code pat_med_inhosp} çš„哪个 check_flag"这件事。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th><th>行数</th></tr>
 *   <tr><td>{@link #updateCheckFlag(VisitInhospRow, Integer, String, String)}</td>
 *       <td>{@code updateCheckFlag}(L1660-1684)</td><td>25</td></tr>
 *   <tr><td>{@link #setFlag}</td><td>{@code writeInSubTask} é‡Œé€å¤„
 *       {@code patMedInhosp2.setXxxcheckFlag(...)} + {@code updatePatMedInhosp}</td><td>—</td></tr>
 * </table>
 *
 * <h3>⚠️ type â†’ åˆ—的映射必须原样保留(含历史交叉写)</h3>
 * åŽŸ {@code updateCheckFlag} çš„ switch(L1663-1679):
 * <pre>
 * 1 -> deptcheck_flag        2 -> wardcheck_flag       3 -> diagcheck_flag
 * 4 -> in_deptcheck_flag     5 -> in_wardcheck_flag
 * </pre>
 * è€Œ {@code writeInSubTask} é‡Œå›žå†™æ—¶ä¼šæ ¹æ® type å•独 set,且存在<b>历史交叉写</b>:
 * <ul>
 *   <li>type=5 çš„"任务不存在"分支(L721-722)写的是 {@code inWardcheckFlag};</li>
 *   <li>type=5 çš„"成功/失败回写"(L885/L896)写的也是 {@code inWardcheckFlag};</li>
 *   <li>但 {@code dealOutHospInfo} çš„病区宣教分支写的是 {@code in_deptcheck_flag}(见 ruoyi-publicity)。</li>
 * </ul>
 * æ‰€ä»¥è¿™é‡Œ<b>不统一</b>:{@link #updateCheckFlag} ç…§æŠ„原 switch;
 * å„调用点要写哪一列由它自己用 {@link #setFlag} æ˜¾å¼æŒ‡å®šã€‚任何"顺手统一"都是行为变更。
 *
 * <h3>为什么单独成类而不是塞进 VisitSubtaskFactory</h3>
 * å·¥åŽ‚æ˜¯"纯组装"(可被 A æ¡£é€è¡Œæ¯”对);本类是"写库"。
 * æ··åœ¨ä¸€èµ·ä¼šè®©"组装器一个副作用都没有"这个前提失效,探针也就失去意义。
 */
@Slf4j
@Component
public class InhospFlagWriter {
    /** å…è®¸å›žå†™çš„æ ‡å¿—位列白名单(与 {@code VisitInhospMapper.updateFlags} çš„ {@code <choose>} ä¸€è‡´ï¼‰ */
    private static final Set<String> ALLOWED_FLAG_COLUMNS = Collections.unmodifiableSet(
            new HashSet<>(Arrays.asList(
                    "deptcheck_flag", "wardcheck_flag", "diagcheck_flag", "medcheck_flag",
                    "in_deptcheck_flag", "in_wardcheck_flag", "schedulecheck_flag")));
    @Autowired
    private VisitInhospMapper inhospMapper;
    /**
     * ç»Ÿä¸€å›žå†™æ ‡å¿—位 â€”— ç…§æŠ„原 {@code updateCheckFlag(PatMedInhosp, Integer type, String flagValue, String remark)}。
     * <p>
     * åŽŸå®žçŽ°ï¼ˆL1660-1684):
     * <pre>
     * PatMedInhosp updateObj = new PatMedInhosp();
     * updateObj.setInhospid(patMedInhosp.getInhospid());
     * switch (type) { case 1: updateObj.setDeptcheckFlag(flagValue); break; ... }
     * if (StringUtils.isNotEmpty(remark)) {
     *     updateObj.setRemark(remark + " (type=" + type + ")");
     * }
     * patMedInhospMapper.updatePatMedInhosp(updateObj);
     * </pre>
     * âš ï¸ æ³¨æ„ä¸¤ç‚¹ï¼š
     * <ol>
     *   <li>它写的是 {@code remark}(不是 {@code long_task_reason})——原样保留;</li>
     *   <li>remark éžç©ºæ—¶ä¼šæ‹¼ä¸Š {@code " (type=N)"} åŽç¼€ â€”— åŽŸæ ·ä¿ç•™è¿™ä¸ªæ ¼å¼ã€‚</li>
     * </ol>
     *
     * @param row       å‡ºå…¥é™¢è®°å½•(只用 inhospid)
     * @param type      1=离院科室 2=离院病区 3=疾病 4=入院科室 5=入院病区
     * @param flagValue è¦å†™å…¥çš„æ ‡å¿—位值
     * @param remark    å¤‡æ³¨ï¼›éžç©ºæ—¶ä¼šæ‹¼ {@code " (type=N)"} åŽç¼€åŽå†™å…¥ remark åˆ—
     */
    public void updateCheckFlag(VisitInhospRow row, Integer type, String flagValue, String remark) {
        String column = columnOfType(type);
        if (column == null) {
            log.warn("【出入院随访】updateCheckFlag æ”¶åˆ°æœªçŸ¥ type={},忽略。inhospid={}",
                    type, row == null ? null : row.getInhospid());
            return;
        }
        String finalRemark = remark;
        if (com.ruoyi.common.utils.StringUtils.isNotEmpty(remark)) {
            finalRemark = remark + " (type=" + type + ")";
        }
        // åŽŸå®žçŽ°èµ°çš„æ˜¯"整实体动态更新"(会顺带把 remark å†™è¿›åŽ»ï¼‰ã€‚
        // è¿™é‡Œ updateFlags åªè¦†ç›– flag + long_task_reason,所以 remark éœ€è¦å•独写。
        setFlagWithRemark(row, column, flagValue, finalRemark);
    }
    /**
     * åªå›žå†™ä¸€ä¸ªæ ‡å¿—位(对应 {@code writeInSubTask} é‡Œé€å¤„ set å•个 flag çš„写法)。
     * <p>
     * åŽŸå®žçŽ°çš„å†™æ³•æ˜¯ï¼šæ–°å»ºä¸€ä¸ª"只有 inhospid + è¦æ”¹çš„ flag"的 {@code PatMedInhosp},
     * å†è°ƒ {@code updatePatMedInhosp}(XML é‡Œæ¯ä¸ªå­—段各自 {@code <if test="xxx != null">},
     * æ‰€ä»¥åªæœ‰éž null å­—段生效)。
     *
     * @param inhospid å‡ºå…¥é™¢è®°å½•ID
     * @param column   æ ‡å¿—位列名(白名单校验)
     * @param value    æ ‡å¿—位值
     * @return true = å½±å“è¡Œæ•° > 0
     */
    public boolean setFlag(Long inhospid, String column, String value) {
        if (!ALLOWED_FLAG_COLUMNS.contains(column)) {
            log.warn("【出入院随访】setFlag æ”¶åˆ°éžæ³•列名「{}」,忽略。inhospid={}", column, inhospid);
            return false;
        }
        return inhospMapper.updateFlags(inhospid, column, value, null) > 0;
    }
    /**
     * å›žå†™æ ‡å¿—位 + è¦†ç›–式写 {@code long_task_reason}。
     * <p>
     * å¯¹åº” {@code writeInSubTask} é‡Œ"任务不存在"/"异常"分支的写法:
     * å…ˆ {@code setLongTaskReason(旧值 + æ–‡æ¡ˆ)} å† update。
     * æ³¨æ„ï¼šé‚£äº›åˆ†æ”¯åœ¨ Java ä¾§æ˜¯ {@code longTaskReason + "..."} çš„字符串拼接
     * ï¼ˆå¤šæ•°ä¸åˆ¤ null),所以这里由调用方传入拼好的完整值。
     *
     * @param inhospid å‡ºå…¥é™¢è®°å½•ID
     * @param column   æ ‡å¿—位列名
     * @param value    æ ‡å¿—位值
     * @param reason   {@code long_task_reason} çš„æ–°å€¼ï¼›null = ä¸æ”¹åЍ
     */
    public boolean setFlagWithReason(Long inhospid, String column, String value, String reason) {
        if (!ALLOWED_FLAG_COLUMNS.contains(column)) {
            log.warn("【出入院随访】setFlagWithReason æ”¶åˆ°éžæ³•列名「{}」,忽略。inhospid={}", column, inhospid);
            return false;
        }
        return inhospMapper.updateFlags(inhospid, column, value, reason) > 0;
    }
    /**
     * æ ‡å¿—位列 + remark ä¸€èµ·å†™ï¼ˆåŽŸ {@code updateCheckFlag} çš„实际效果)。
     * <p>
     * {@code VisitInhospMapper.updateFlags} ä¸è¦†ç›– remark,所以这里复用它写 flag,
     * å¦å¤–单独更新 remark â€”— ä¸¤ä¸ª update ä¸ŽåŽŸå®žçŽ°çš„"一次整实体 update"在结果上等价
     * ï¼ˆåŽŸå®žçŽ°ä¹Ÿåªä¼šæœ‰ flag ä¸Ž remark ä¸¤ä¸ªå­—段非 null)。
     */
    /**
     * å›žå†™æ ‡å¿—位 + è¦†ç›–式写 {@code remark}(原 {@code updateCheckFlag} çš„实际效果)。
     * <p>
     * {@code VisitInhospMapper.updateFlags} ä¸è¦†ç›– remark,所以这里复用它写 flag,
     * å¦å¤–单独更新 remark â€”— ä¸¤ä¸ª update ä¸ŽåŽŸå®žçŽ°çš„"一次整实体 update"在结果上等价
     * ï¼ˆåŽŸå®žçŽ°é‚£æ¬¡è°ƒç”¨ä¹Ÿåªæœ‰ flag ä¸Ž remark ä¸¤ä¸ªå­—段非 null)。
     *
     * @param inhospid  å‡ºå…¥é™¢è®°å½•ID
     * @param column    æ ‡å¿—位列名(白名单校验)
     * @param flagValue æ ‡å¿—位值
     * @param remark    æ–°çš„ remark;null è¡¨ç¤ºåªå†™ flag
     */
    public void setFlagWithRemark(Long inhospid, String column, String flagValue, String remark) {
        if (!ALLOWED_FLAG_COLUMNS.contains(column)) {
            log.warn("【出入院随访】setFlagWithRemark æ”¶åˆ°éžæ³•列名「{}」,忽略。inhospid={}", column, inhospid);
            return;
        }
        if (inhospid == null) {
            log.warn("【出入院随访】setFlagWithRemark ç¼ºå°‘ inhospid,忽略。column={}", column);
            return;
        }
        inhospMapper.updateFlags(inhospid, column, flagValue, null);
        if (remark != null) {
            inhospMapper.updateRemark(inhospid, remark);
        }
    }
    /**
     * æ ‡å¿—位列 + remark ä¸€èµ·å†™ï¼ˆ{@link #updateCheckFlag} ç”¨ï¼Œå…¥å‚是行对象)。
     */
    private void setFlagWithRemark(VisitInhospRow row, String column, String flagValue, String remark) {
        if (row == null || row.getInhospid() == null) {
            log.warn("【出入院随访】setFlagWithRemark ç¼ºå°‘ inhospid,忽略。column={}", column);
            return;
        }
        setFlagWithRemark(row.getInhospid(), column, flagValue, remark);
    }
    /**
     * type â†’ æ ‡å¿—位列。照抄原 {@code updateCheckFlag} çš„ switch。
     *
     * @return åˆ—名;未知 type è¿”回 {@code null}
     */
    private String columnOfType(Integer type) {
        if (type == null) {
            return null;
        }
        switch (type) {
            case 1:
                return "deptcheck_flag";
            case 2:
                return "wardcheck_flag";
            case 3:
                return "diagcheck_flag";
            case 4:
                return "in_deptcheck_flag";
            case 5:
                return "in_wardcheck_flag";
            default:
                return null;
        }
    }
}
ruoyi-inhospvisit/src/main/java/com/ruoyi/inhospvisit/support/VisitSubtaskFactory.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,410 @@
package com.ruoyi.inhospvisit.support;
import com.ruoyi.common.core.service.dto.VisitSubtaskDraft;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.inhospvisit.domain.row.VisitInhospRow;
import com.ruoyi.inhospvisit.domain.row.VisitTaskRow;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Calendar;
import java.util.Date;
/**
 * éšè®¿å­ä»»åŠ¡ã€Œç»„è£…å™¨ã€â€”â€” é€è¡Œæ¬è‡ª {@code PatMedInhospServiceImpl} çš„两个 boxed æ–¹æ³•。
 *
 * <h3>来源对照(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th><th>行数</th></tr>
 *   <tr><td>{@link #boxedServiceSubtask}</td><td>{@code boxedServiceSubtask}(L1108-1222)</td><td>115</td></tr>
 *   <tr><td>{@link #boxedServiceSubtaskForOp}</td><td>{@code boxedServiceSubtaskForOp}(L1225-1300)</td><td>76</td></tr>
 *   <tr><td>{@link #addDays}</td><td>{@code addDays}(L663-671)</td><td>9</td></tr>
 *   <tr><td>{@link #minusDay}</td><td>{@code minusDay}(L1309-1318)</td><td>10</td></tr>
 *   <tr><td>{@link #updateCheckFlag}</td><td>{@code updateCheckFlag}(L1660-1684)</td><td>25</td></tr>
 * </table>
 *
 * <h3>⚠️ ä¸‰ä¸ªæœ€å®¹æ˜“被漏掉的语义(本类已逐个显式处理)</h3>
 * <ol>
 *   <li><b>{@code DtoConversionUtils.sourceToTarget} çš„隐性字段搬运</b>:
 *       åŽŸå®žçŽ°ç¬¬ä¸€è¡ŒæŠŠ {@code ServiceTask}→{@code ServiceSubtask} çš„ 33 ä¸ªåŒåå­—段整批拷过来,
 *       å…¶ä¸­ <b>18 ä¸ªä¹‹åŽå†ä¹Ÿæ²¡è¢«æ˜¾å¼è¦†ç›–</b>({@code delFlag diagType endOutHospTime guid isupload
 *       leavehospitaldistrictcodes leaveldeptcodes libtemplatename pageNum pageSize pid sendType
 *       startOutHospTime taskDesc taskName textParam type uploadTime})。
 *       æœ¬ç±»æŠŠè¿™ 18 ä¸ª<b>逐个显式</b>搬进 {@link VisitSubtaskDraft},见
 *       {@link #applyCarriedOverFieldsFromTask}。少一个就会静默丢值。</li>
 *   <li><b>两个方法失败时的返回语义不同</b>:
 *       {@code boxedServiceSubtask} å‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶è¿”回 <b>{@code new ServiceSubtask()} ç©ºå¯¹è±¡</b>;
 *       {@code boxedServiceSubtaskForOp} è¿”回 <b>{@code null}</b>。
 *       æ‰€ä»¥æœ¬ç±»ç¬¬ä¸€ä¸ªè¿”回"空草稿",第二个返回 {@code null}。</li>
 *   <li><b>第一个方法会<b>顺手写库</b></b>:出院时间为空时它把
 *       {@code deptcheckFlag="2"} ä¸Ž {@code remark} å†™å›ž {@code pat_med_inhosp}。
 *       è¿™å±žäºŽ"副作用",本类通过 {@link BoxedSideEffect} å›žè°ƒäº¤å›žè°ƒç”¨æ–¹æ‰§è¡Œï¼Œ
 *       é¿å…ç»„装器直接依赖 Mapper。</li>
 * </ol>
 *
 * <h3>⚠️ ä¸Žæ–¹æ¡ˆçš„差异(已登记)</h3>
 * æ–¹æ¡ˆåŽŸè®¡åˆ’å« {@code VisitSubtaskFactory} å¹¶åŒæ—¶æ‰¿è½½ {@code writeInSubTask}。
 * å®žé™…实现时把它收窄为"纯组装 + çº¯å·¥å…·",因为 {@code writeInSubTask}(232 è¡Œï¼‰é‡Œæœ‰å¤§é‡
 * å†™åº“与去重编排,混在一起会让本类的 A æ¡£é€è¡Œç­‰ä»·æŽ¢é’ˆå¤±åŽ»æ„ä¹‰ã€‚
 */
@Slf4j
@Component
public class VisitSubtaskFactory {
    /** å‡ºé™¢éšè®¿ï¼ˆåŠä¸“病随访)的任务类型值:通知类,需要提前 N å¤©å‘ */
    private static final String TASK_TYPE_NOTICE = "3";
    /** å…¥é™¢å®£æ•™çš„æœåŠ¡ç±»åž‹ */
    private static final String SERVICE_TYPE_IN_HOSP_PUBLICITY = "17";
    /**
     * æ™¯å®æœºæž„ID(原实现里硬编码的白名单,原样保留)。
     * <p>
     * âš ï¸ è¿™ä¸ªå€¼æˆ‘第一版就抄错了一位(写成了 {@code ...1271011A1001})。
     * å·²é€å­—核对原实现出现过的 4 å¤„:
     * {@code PatMedInhospServiceImpl} L798 / L1186(注释) / L1195 / L1338,均为
     * {@code 47255004333112711A1001}。抄错会导致"景宁的白名单分支永远不生效"。
     */
    private static final String ORG_JINGNING = "47255004333112711A1001";
    @Value("${spring.profiles.active}")
    private String active;
    /**
     * ç»„装院外随访(科室/病区/疾病/宣教/药品)的子任务草稿。
     * <p>
     * é€è¡Œå¯¹åº” {@code boxedServiceSubtask(ServiceTask, PatMedInhosp, PatArchive, String, Integer)} L1108-1222。
     *
     * @param serviceTask é•¿æœŸä»»åŠ¡
     * @param inhosp      å‡ºå…¥é™¢è®°å½•
     * @param archive     æ‚£è€…档案
     * @param config      {@code sys_config: visit.early.day}
     * @param type        0=药品宣教 / 1=离院科室 / 2=离院病区 / 3=疾病 / 4=入院科室 / 5=入院病区
     * @param sideEffect  å‰¯ä½œç”¨å›žè°ƒï¼ˆå‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶è¦æŠŠæ ‡å¿—位与备注写回库);可为 {@code null}(测试用)
     * @return ç»„装好的草稿;<b>出院时间为空且 type∉{4,5} æ—¶è¿”回"空草稿"</b>(对应原实现 {@code new ServiceSubtask()})
     */
    public VisitSubtaskDraft boxedServiceSubtask(VisitTaskRow serviceTask, VisitInhospRow inhosp,
                                                 VisitArchiveRow archive, String config, Integer type,
                                                 BoxedSideEffect sideEffect) {
        VisitSubtaskDraft d = new VisitSubtaskDraft();
        // ---- åŽŸ L1113:DtoConversionUtils çš„æ•´æ‰¹æ‹·è´ï¼ˆ18 ä¸ªå­—段靠它带过来)----
        applyCarriedOverFieldsFromTask(d, serviceTask);
        // ---- åŽŸ L1114-1118 ----
        d.setTaskid(serviceTask.getTaskid());
        d.setOrgid(serviceTask.getOrgid());
        d.setCampusid(serviceTask.getCampusid());
        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid())) {
            d.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
        }
        // ---- åŽŸ L1119-1127 ----
        d.setNurseId(inhosp.getNurseId());
        d.setNurseName(inhosp.getNurseName());
        d.setInhospid(inhosp.getInhospid());
        d.setDrcode(inhosp.getDrcode());
        d.setDrname(inhosp.getDrname());
        d.setDeptcode(inhosp.getLeaveldeptcode());
        d.setDeptname(inhosp.getLeaveldeptname());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        // ---- åŽŸ L1128-1134:type 4/5 æ”¹ç”¨å…¥é™¢ç»´åº¦ ----
        if (type != null && (type == 4 || type == 5)) {
            d.setDeptcode(inhosp.getDeptcode());
            d.setDeptname(inhosp.getDeptname());
            d.setLeavehospitaldistrictcode(inhosp.getHospitaldistrictcode());
            d.setLeavehospitaldistrictname(inhosp.getHospitaldistrictname());
        }
        // ---- åŽŸ L1135-1146 ----
        d.setLeaveicd10code(inhosp.getLeaveicd10code());
        d.setLeavediagname(inhosp.getLeavediagname());
        d.setDiagname(inhosp.getDiagname());
        d.setTemplateid(serviceTask.getTemplateid());
        d.setTemplatename(serviceTask.getTemplatename());
        d.setPatid(archive.getId());
        d.setSendname(archive.getName());
        d.setSfzh(archive.getIdcardno());
        d.setPhone(archive.getTelcode());
        if (StringUtils.isBlank(archive.getTelcode())) {
            d.setPhone(archive.getRelativetelcode());
        }
        d.setSex(archive.getSex());
        d.setAge(archive.getAge());
        // ---- åŽŸ L1147-1163 ----
        d.setSendstate(2L);
        d.setCampusid(inhosp.getCampusid());
        d.setManagementDoctor(inhosp.getManagementDoctor());
        d.setManagementDoctorCode(inhosp.getManagementDoctorCode());
        d.setStarttime(inhosp.getStarttime());
        d.setEndtime(inhosp.getEndtime());
        d.setServiceType(serviceTask.getServiceType());
        d.setPreachform(serviceTask.getPreachform());
        d.setHospType("2");
        if (type != null && (type == 4 || type == 5)) {
            d.setHospType("6");
        }
        d.setCreateTime(new Date());
        d.setUpdateTime(new Date());
        d.setCreateBy(inhosp.getNurseName());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        d.setUpdateBy(serviceTask.getUpdateBy());
        d.setUpdateTime(new Date());
        // åŽŸ L1164-1165:如果是宣教就设置为7
        // âš ï¸ åŽŸå®žçŽ°ç›´æŽ¥ getServiceType().equals("17"),serviceType ä¸º null ä¼š NPE â€”— åŽŸæ ·ä¿ç•™è¯¥é£Žé™©
        if (serviceTask.getServiceType() != null
                && SERVICE_TYPE_IN_HOSP_PUBLICITY.equals(serviceTask.getServiceType())) {
            d.setTaskSituation(7);
        }
        // ---- åŽŸ L1166-1182:发送时间 ----
        if (serviceTask.getSendDay() == null) {
            serviceTask.setSendDay(1L);
        }
        Date newDate;
        if (inhosp.getEndtime() != null) {
            newDate = addDays(inhosp.getEndtime(), serviceTask.getSendDay().intValue());
        } else {
            // å¦‚果等于4/5说明是入院宣教,入院不可能有 endTime
            if (type == null || (type != 4 && type != 5)) {
                // åŽŸ L1174-1178:★副作用:写回 deptcheckFlag="2" + remark,然后返回【空草稿】
                if (sideEffect != null) {
                    sideEffect.onMissingOutHospTime(inhosp);
                }
                return new VisitSubtaskDraft();
            }
            // å…¥é™¢å®£æ•™ï¼ŒåŸºæœ¬å½“天或隔天就要发出
            newDate = addDays(new Date(), serviceTask.getSendDay().intValue());
        }
        d.setLongSendTime(newDate);
        d.setVisitTime(newDate);
        // ---- åŽŸ L1187-1203:通知类提前 N å¤© ----
        if (TASK_TYPE_NOTICE.equals(serviceTask.getType())) {
            d.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
            if (inhosp.getFudate() != null) {
                d.setVisitTime(minusDay(inhosp.getFudate(), StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
            } else {
                // åˆ¤æ–­æ˜¯ä¸æ˜¯æ™¯å®
                if (ORG_JINGNING.equals(inhosp.getOrgid())) {
                    d.setVisitTime(null);
                    // ä¸æ‰§è¡Œ
                    d.setSendstate(4L);
                    d.setRemark("景宁没有给随访时间,不发送通知短信");
                }
            }
        }
        // ---- åŽŸ L1204-1210:南华附一:入院出院同一天不随访 ----
        if ("nhfy".equals(active) && inhosp.getStarttime() != null && inhosp.getEndtime() != null
                && daysBetweenByMillisecond(inhosp.getStarttime(), inhosp.getEndtime()) == 0) {
            d.setRemark("患者当天出院,不用随访");
            d.setResult("error");
            // ä¸æ‰§è¡Œ
            d.setSendstate(4L);
        }
        // ---- åŽŸ L1212-1220:患者维度的"免随访"标记 ----
        if (StringUtils.isNotEmpty(archive.getNotrequiredFlag()) && "1".equals(archive.getNotrequiredFlag())) {
            d.setRemark(archive.getNotrequiredreason());
            d.setResult("error");
            d.setTaskSituation(6);
            // ä¸æ‰§è¡Œ
            d.setSendstate(4L);
        }
        return d;
    }
    /**
     * ç»„装手术随访的子任务草稿。
     * <p>
     * é€è¡Œå¯¹åº” {@code boxedServiceSubtaskForOp(ServiceTask, PatMedInhosp, PatMedOperationItem, PatArchive, String)}
     * L1225-1300。
     * <p>
     * âš ï¸ ä¸Ž {@link #boxedServiceSubtask} çš„两处关键差异:
     * <ol>
     *   <li>出院时间为空时返回 <b>{@code null}</b>(不是空草稿),并且写的是
     *       {@code pat_med_operation_item.opercheck_flag="2"} è€Œ<b>不是</b> inhosp çš„æ ‡å¿—位;</li>
     *   <li>不使用 type å‚数,也不设 {@code taskSituation}/{@code hospType="6"}。</li>
     * </ol>
     *
     * @param operationItemId æ‰‹æœ¯æ˜Žç»†ID(原 {@code patMedOperationItem.getId()})
     * @param onMissingOutHospTime å‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶çš„副作用回调;可为 {@code null}
     * @return è‰ç¨¿ï¼›å‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶è¿”回 {@code null}
     */
    public VisitSubtaskDraft boxedServiceSubtaskForOp(VisitTaskRow serviceTask, VisitInhospRow inhosp,
                                                      Long operationItemId, VisitArchiveRow archive,
                                                      String config,
                                                      java.util.function.Consumer<VisitInhospRow> onMissingOutHospTime) {
        VisitSubtaskDraft d = new VisitSubtaskDraft();
        // ---- åŽŸ L1226:拷贝 ----
        applyCarriedOverFieldsFromTask(d, serviceTask);
        // ---- åŽŸ L1227-1231 ----
        d.setTaskid(serviceTask.getTaskid());
        d.setOrgid(serviceTask.getOrgid());
        d.setCampusid(serviceTask.getCampusid());
        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid())) {
            d.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
        }
        // ---- åŽŸ L1232-1240 ----
        d.setNurseId(inhosp.getNurseId());
        d.setNurseName(inhosp.getNurseName());
        d.setInhospid(inhosp.getInhospid());
        d.setDrcode(inhosp.getDrcode());
        d.setDrname(inhosp.getDrname());
        d.setDeptcode(inhosp.getLeaveldeptcode());
        d.setDeptname(inhosp.getLeaveldeptname());
        d.setLeavehospitaldistrictcode(inhosp.getLeavehospitaldistrictcode());
        d.setLeavehospitaldistrictname(inhosp.getLeavehospitaldistrictname());
        // ---- åŽŸ L1242-1251 ----
        d.setTemplateid(serviceTask.getTemplateid());
        d.setTemplatename(serviceTask.getTemplatename());
        d.setPatid(archive.getId());
        d.setSendname(archive.getName());
        d.setSfzh(archive.getIdcardno());
        d.setPhone(archive.getTelcode());
        if (StringUtils.isBlank(archive.getTelcode())) {
            d.setPhone(archive.getRelativetelcode());
        }
        d.setSex(archive.getSex());
        d.setAge(archive.getAge());
        d.setSendstate(2L);
        d.setManagementDoctor(inhosp.getDrname());
        d.setManagementDoctorCode(inhosp.getDrcode());
        // ---- åŽŸ L1255-1266 ----
        d.setStarttime(inhosp.getStarttime());
        d.setEndtime(inhosp.getEndtime());
        d.setServiceType(serviceTask.getServiceType());
        d.setPreachform(serviceTask.getPreachform());
        d.setHospType("2");
        d.setCreateTime(new Date());
        d.setUpdateTime(new Date());
        d.setCreateBy(inhosp.getNurseName());
        d.setOperationItemId(operationItemId);
        d.setUpdateBy(serviceTask.getUpdateBy());
        d.setUpdateTime(new Date());
        // ---- åŽŸ L1268-1282:发送时间 ----
        if (serviceTask.getSendDay() == null) {
            serviceTask.setSendDay(1L);
        }
        Date newDate;
        if (inhosp.getEndtime() != null) {
            newDate = addDays(inhosp.getEndtime(), serviceTask.getSendDay().intValue());
        } else {
            // åŽŸ L1274-1281:★副作用落在 pat_med_operation_item ä¸Šï¼Œç”±è°ƒç”¨æ–¹æ‰§è¡Œ
            if (onMissingOutHospTime != null) {
                onMissingOutHospTime.accept(inhosp);
            }
            // å¦‚果出院时间为空,则返回 null(注意:不是空草稿)
            return null;
        }
        d.setLongSendTime(newDate);
        d.setVisitTime(newDate);
        // ---- åŽŸ L1286-1289:通知类提前 N å¤© ----
        if (TASK_TYPE_NOTICE.equals(serviceTask.getType())) {
            d.setVisitTime(minusDay(newDate, StringUtils.isNotEmpty(config) ? Integer.parseInt(config) : 0));
        }
        // ---- åŽŸ L1290-1298:免随访标记 ----
        if (StringUtils.isNotEmpty(archive.getNotrequiredFlag()) && "1".equals(archive.getNotrequiredFlag())) {
            d.setRemark(archive.getNotrequiredreason());
            d.setResult("error");
            d.setTaskSituation(6);
            d.setSendstate(4L);
        }
        return d;
    }
    /**
     * æŠŠ {@code DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class)} é‡Œ
     * <b>拷入但之后再未被显式覆盖</b>的 18 ä¸ªå­—段显式搬过来。
     * <p>
     * âš ï¸ è¿™ 18 ä¸ªå­—段是"隐式"进子任务的:原实现没有一行 set å®ƒä»¬ï¼Œ
     * å…¨é  DtoConversionUtils çš„同名拷贝。搬迁后必须显式列出,否则值会丢。
     * æ¸…单由脚本从 {@code ServiceTask âˆ© ServiceSubtask} å‡åŽ»"被显式 set è¿‡çš„"算出,
     * å¹¶ç”± {@code sql/_probe/verify_inhospvisit_semantics.py} æ–­è¨€ã€‚
     */
    private void applyCarriedOverFieldsFromTask(VisitSubtaskDraft d, VisitTaskRow t) {
        d.setDelFlag(t.getDelFlag());
        d.setDiagType(t.getDiagType());
        d.setEndOutHospTime(t.getEndOutHospTime());
        d.setGuid(t.getGuid());
        d.setIsupload(t.getIsupload());
        d.setLeavehospitaldistrictcodes(t.getLeavehospitaldistrictcodes());
        d.setLeaveldeptcodes(t.getLeaveldeptcodes());
        d.setLibtemplatename(t.getLibtemplatename());
        d.setPageNum(t.getPageNum());
        d.setPageSize(t.getPageSize());
        d.setPid(t.getPid() == null ? null : t.getPid().longValue());
        d.setSendType(t.getSendType() == null ? null : String.valueOf(t.getSendType()));
        d.setStartOutHospTime(t.getStartOutHospTime());
        d.setTaskDesc(t.getTaskDesc());
        d.setTaskName(t.getTaskName());
        d.setTextParam(t.getTextParam());
        d.setType(t.getType());
        d.setUploadTime(t.getUploadTime());
    }
    // ==========================================================================
    // å·¥å…·æ–¹æ³•(原样照抄,含已知缺陷)
    // ==========================================================================
    /**
     * æ—¥æœŸåŠ å¤©æ•°ï¼ˆåŽŸ {@code addDays},L663-671)。{@code days} ä¸º null æ—¶æŒ‰ 1 å¤„理。
     */
    public Date addDays(Date date, Integer days) {
        if (days == null) {
            days = 1;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }
    /**
     * å‡åŽ»ç›¸åº”å¤©æ•°ï¼ˆåŽŸ {@code minusDay},L1309-1318)。
     * <p>
     * âš ï¸ <b>已知缺陷,原样保留</b>:原实现写死 {@code instant.minus(2, ChronoUnit.DAYS)},
     * <b>完全忽略 {@code day} å…¥å‚</b>。调用点传的是 {@code sys_config: visit.early.day},
     * ä½†å®žé™…效果永远是"减 2 å¤©"。
     * <p>
     * <b>不修</b>:修了会改变线上通知短信的发送时间,属可见行为变更。
     */
    public Date minusDay(Date originalDate, Integer day) {
        // åŽŸæ ·ç…§æŠ„ï¼šå…¥å‚ day è¢«å¿½ç•¥ï¼Œå›ºå®šå‡ 2 å¤©
        java.time.Instant instant = originalDate.toInstant();
        java.time.Instant twoDaysAgoInstant = instant.minus(2, java.time.temporal.ChronoUnit.DAYS);
        return Date.from(twoDaysAgoInstant);
    }
    /**
     * ä¸¤ä¸ª {@code Date} ç›¸å·®å¤šå°‘天(原 {@code DateUtils.differentDaysByMillisecond})。
     * <p>
     * åŽŸå®žçŽ° {@code boxedServiceSubtask} L1205 ç›´æŽ¥è°ƒ {@code DateUtils.differentDaysByMillisecond},
     * è¯¥å·¥å…·åœ¨ ruoyi-common,新模块可以直接用 â€”— è¿™é‡Œåªæ˜¯æŠŠè°ƒç”¨ç‚¹æ”¶æ•›åˆ°ä¸€å¤„,
     * ä¾¿äºŽæŽ¢é’ˆæ–­è¨€"用的是同一个工具"。
     */
    private int daysBetweenByMillisecond(Date start, Date end) {
        return com.ruoyi.common.utils.DateUtils.differentDaysByMillisecond(start, end);
    }
    /**
     * ç»„装器的副作用回调(原 {@code boxedServiceSubtask} å‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶ä¼šé¡ºæ‰‹å†™åº“)。
     * <p>
     * æŠ½æˆå›žè°ƒæ˜¯ä¸ºäº†è®©ç»„装器<b>不直接依赖 Mapper</b>,保持"纯组装"可被逐行比对。
     * çœŸæ­£çš„写库由 {@code InhospVisitTaskService} å®žçŽ°ã€‚
     */
    public interface BoxedSideEffect {
        /**
         * å‡ºé™¢æ—¶é—´ä¸ºç©ºæ—¶çš„回写:原 L1174-1176
         * <pre>
         * patMedInhosp1.setDeptcheckFlag("2");
         * patMedInhosp1.setRemark(patMedInhosp1.getRemark() + "&amp; è¯¥æ‚£è€…疾病出院时间为空,出院记录ID为:" + patMedInhosp1.getInhospid());
         * patMedInhospMapper.updatePatMedInhosp(patMedInhosp1);
         * </pre>
         */
        void onMissingOutHospTime(VisitInhospRow inhosp);
    }
}
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/PatMedInhospMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1181 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inhospvisit.mapper.PatMedInhospMapper">
    <resultMap type="com.ruoyi.inhospvisit.domain.PatMedInhosp" id="PatMedInhospResult">
        <result property="inhospid" column="inhospid"/>
        <result property="inhospno" column="inhospno"/>
        <result property="serialnum" column="serialnum"/>
        <result property="hospitalname" column="hospitalname"/>
        <result property="hospitalcode" column="hospitalcode"/>
        <result property="hospitaldistrictcode" column="hospitaldistrictcode"/>
        <result property="hospitaldistrictname" column="hospitaldistrictname"/>
        <result property="icd10code" column="icd10code"/>
        <result property="diagname" column="diagname"/>
        <result property="starttime" column="starttime"/>
        <result property="endtime" column="endtime"/>
        <result property="deptcode" column="deptcode"/>
        <result property="deptname" column="deptname"/>
        <result property="roomno" column="roomno"/>
        <result property="bedNo" column="bed_no"/>
        <result property="orgid" column="orgid"/>
        <result property="delFlag" column="del_flag"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="isupload" column="isupload"/>
        <result property="uploadTime" column="upload_time"/>
        <result property="patid" column="patid"/>
        <result property="leavediagname" column="leavediagname"/>
        <result property="leaveicd10code" column="leaveicd10code"/>
        <result property="drcode" column="drcode"/>
        <result property="drname" column="drname"/>
        <result property="schemestatus" column="schemestatus"/>
        <result property="generalschemestatus" column="generalschemestatus"/>
        <result property="leaveldeptcode" column="leaveldeptcode"/>
        <result property="leaveldeptname" column="leaveldeptname"/>
        <result property="hospitaldistrictid" column="hospitaldistrictid"/>
        <result property="leavehospitaldistrictcode" column="leavehospitaldistrictcode"/>
        <result property="leavehospitaldistrictname" column="leavehospitaldistrictname"/>
        <result property="leavehospitaldistrictid" column="leavehospitaldistrictid"/>
        <result property="deptid" column="deptid"/>
        <result property="leaveldeptid" column="leaveldeptid"/>
        <result property="schemetime" column="schemetime"/>
        <result property="patname" column="patname"/>
        <result property="patno" column="patno"/>
        <result property="inhospstate" column="inhospstate"/>
        <result property="deptcheckFlag" column="deptcheck_flag"/>
        <result property="inWardcheckFlag" column="in_wardcheck_flag"/>
        <result property="inDeptcheckFlag" column="in_deptcheck_flag"/>
        <result property="wardcheckFlag" column="wardcheck_flag"/>
        <result property="diagcheckFlag" column="diagcheck_flag"/>
        <result property="scheduleCheckFlag" column="schedulecheck_flag"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="telcode" column="telcode"/>
        <result property="longTaskReason" column="long_task_reason"/>
        <result property="nurseId" column="nurse_id"/>
        <result property="nurseName" column="nurse_name"/>
        <result property="outWayId" column="out_way_id"/>
        <result property="outWayName" column="out_way_name"/>
        <result property="guid" column="guid"/>
        <result property="operator" column="operator"/>
        <result property="operatorId" column="operator_id"/>
        <result property="remark" column="remark"/>
        <result property="tagname" column="tagname"/>
        <result property="fuflag" column="fuflag"/>
        <result property="fudate" column="fudate"/>
        <result property="fuperiod" column="fuperiod"/>
        <result property="futypecode" column="futypecode"/>
        <result property="futypedesc" column="futypedesc"/>
        <result property="fuadvice" column="fuadvice"/>
        <result property="fuspecialadvice" column="fuspecialadvice"/>
        <result property="managementDoctor" column="management_doctor"/>
        <result property="managementDoctorCode" column="management_doctor_code"/>
        <result property="campusid" column="campusid"/>
        <result property="inhospMydFlag" column="inhosp_myd_flag"/>
        <result property="encounterSourceId" column="encounter_source_id"/>
        <result property="importanceFlagCode" column="importance_flag_code"/>
        <result property="emergencyLevelCode" column="emergency_level_code"/>
        <result property="medicalCode" column="medical_code"/>
        <result property="medicalName" column="medical_name"/>
        <result property="medicalCompany" column="medical_company"/>
    </resultMap>
    <sql id="selectPatMedInhospVo">
        select inhospid,
               medical_code,
               medical_name,
               medical_company,
               encounter_source_id,
               importance_flag_code,
               emergency_level_code,
               patname,
               inhosp_myd_flag,
               campusid,
               fuflag,
               management_doctor,
               management_doctor_code,
               fudate,
               fuperiod,
               futypecode,
               futypedesc,
               fuadvice,
               fuspecialadvice,
               remark,
               guid,
               operator,
               operator_id,
               out_way_id,
               out_way_name,
               nurse_id,
               nurse_name,
               deptcheck_flag,
               wardcheck_flag,
               in_deptcheck_flag,
               in_wardcheck_flag,
               diagcheck_flag,
               schedulecheck_flag,
               inhospstate,
               patno,
               long_task_reason,
               inhospno,
               serialnum,
               hospitalname,
               hospitalcode,
               hospitaldistrictcode,
               hospitaldistrictname,
               icd10code,
               diagname,
               starttime,
               endtime,
               deptcode,
               deptname,
               roomno,
               bed_no,
               orgid,
               del_flag,
               update_by,
               update_time,
               create_by,
               create_time,
               isupload,
               upload_time,
               patid,
               leavediagname,
               leaveicd10code,
               drcode,
               drname,
               schemestatus,
               generalschemestatus,
               leaveldeptcode,
               leaveldeptname,
               hospitaldistrictid,
               leavehospitaldistrictcode,
               leavehospitaldistrictname,
               leavehospitaldistrictid,
               deptid,
               leaveldeptid,
               schemetime
        from pat_med_inhosp
    </sql>
    <select id="selectPatMedInhospList" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhosp" resultMap="PatMedInhospResult">
        select a.age AS age,
        a.telcode as telcode,
        a.sex,
        b.inhospid,
        b.nurse_id,
        b.nurse_name,
        b.patname,
        b.management_doctor,
        b.management_doctor_code,
        b.out_way_id,
        b.out_way_name,
        b.deptcheck_flag,
        b.wardcheck_flag,
        b.in_deptcheck_flag,
        b.in_wardcheck_flag,
        b.diagcheck_flag,
        b.schedulecheck_flag,
        b.inhospstate,
        b.patno,
        b.inhospno,
        b.serialnum,
        b.hospitalname,
        b.hospitalcode,
        b.hospitaldistrictcode,
        b.hospitaldistrictname,
        b.icd10code,
        b.diagname,
        b.starttime,
        b.endtime,
        b.deptcode,
        b.campusid,
        b.deptname,
        b.roomno,
        b.bed_no,
        b.orgid,
        b.del_flag,
        b.update_by,
        b.update_time,
        b.create_by,
        b.create_time,
        b.isupload,
        b.upload_time,
        b.patid,
        b.leavediagname,
        b.leaveicd10code,
        b.drcode,
        b.drname,
        b.schemestatus,
        b.generalschemestatus,
        b.leaveldeptcode,
        b.leaveldeptname,
        b.hospitaldistrictid,
        b.leavehospitaldistrictcode,
        b.leavehospitaldistrictname,
        b.leavehospitaldistrictid,
        b.deptid,
        b.leaveldeptid,
        b.long_task_reason,
        b.schemetime,
        b.fuflag,
        b.fudate,
        b.fuperiod,
        b.futypecode,
        b.futypedesc,
        b.fuadvice,
        b.fuspecialadvice,
        b.inhosp_myd_flag,
        b.encounter_source_id,
        b.importance_flag_code,
        b.emergency_level_code,
        b.medical_code,
        b.medical_name,
        b.medical_company,
        a.idcardno
        FROM
        pat_med_inhosp b
        JOIN
        pat_archive a ON a.id = b.patid
        where 1=1
        AND a.del_flag = 0
        AND b.del_flag = 0
        AND a.idcardno IS NOT NULL
        <if test="inhospid != null">and b.inhospid = #{inhospid}</if>
        <if test="startOutHospTime != null ">and date_format(b.endtime,'%y%m%d') &gt;=
            date_format(#{startOutHospTime},'%y%m%d')
        </if>
        <if test="endOutHospTime != null ">and date_format(b.endtime,'%y%m%d') &lt;=
            date_format(#{endOutHospTime},'%y%m%d')
        </if>
        <if test="startInHospTime != null ">and date_format(b.starttime,'%y%m%d') &gt;=
            date_format(#{startInHospTime},'%y%m%d')
        </if>
        <if test="endInHospTime != null ">and date_format(b.starttime,'%y%m%d') &lt;=
            date_format(#{endInHospTime},'%y%m%d')
        </if>
        <if test="hospitalname != null  and hospitalname != ''">and hospitalname like concat('%', #{hospitalname},
            '%')
        </if>
        <if test="patname != null  and patname != ''">and patname like concat('%', #{patname}, '%')</if>
        <if test="hospitaldistrictname != null  and hospitaldistrictname != ''">and hospitaldistrictname like
            concat('%', #{hospitaldistrictname}, '%')
        </if>
        <if test="endtime != null ">and b.endtime = #{endtime}</if>
        <if test="bedNo != null ">and b.bed_no = #{bedNo}</if>
        <if test="inhospMydFlag != null ">and b.inhosp_myd_flag = #{inhospMydFlag}</if>
        <if test="managementDoctor != null ">and b.management_doctor = #{managementDoctor}</if>
        <if test="managementDoctorCode != null ">and b.management_doctor_code = #{managementDoctorCode}</if>
        <if test="inhospno != null ">and b.inhospno = #{inhospno}</if>
        <if test="fuflag != null ">and b.fuflag = #{fuflag}</if>
        <if test="orgid != null ">and b.orgid = #{orgid}</if>
        <if test="campusid != null ">and b.campusid = #{campusid}</if>
        <if test="serialnum != null ">and b.serialnum = #{serialnum}</if>
        <if test="endtime != null ">and b.endtime = #{endtime}</if>
        <if test="patno != null ">and b.patno = #{patno}</if>
        <if test="nurseId != null ">and b.nurse_id = #{nurseId}</if>
        <if test="nurseName != null and nurseName != ''">and b.nurse_name = #{nurseName}</if>
        <if test="deptcheckFlag != null ">and b.deptcheck_flag = #{deptcheckFlag}</if>
        <if test="scheduleCheckFlag != null ">and b.schedulecheck_flag = #{scheduleCheckFlag}</if>
        <if test="wardcheckFlag != null ">and b.wardcheck_flag = #{wardcheckFlag}</if>
        <if test="inDeptcheckFlag != null ">and b.in_deptcheck_flag = #{inDeptcheckFlag}</if>
        <if test="inWardcheckFlag != null ">and b.in_wardcheck_flag = #{inWardcheckFlag}</if>
        <if test="diagcheckFlag != null ">and b.diagcheck_flag = #{diagcheckFlag}</if>
        <if test="encounterSourceId != null ">and b.encounter_source_id = #{encounterSourceId}</if>
        <if test="importanceFlagCode != null ">and b.importance_flag_code = #{importanceFlagCode}</if>
        <if test="emergencyLevelCode != null ">and b.emergency_level_code = #{emergencyLevelCode}</if>
        <if test="cry != null and cry == 0 ">and b.endtime is null</if>
        <if test="cry != null and cry == 1 ">and b.inhospstate=1</if>
        <if test="cry != null and cry == 2 ">and b.inhospstate=2</if>
        <if test="cry != null and cry == 3 ">and b.inhospstate=3</if>
        <if test="inhospstate != null ">and b.inhospstate = #{inhospstate}</if>
        <if test="schemestatus != null ">and b.schemestatus = #{schemestatus}</if>
        <if test="outWayId != null ">and b.out_way_id = #{outWayId}</if>
        <if test="outWayName != null ">and b.out_way_name = #{outWayName}</if>
        <if test="leaveldeptcode != null ">and b.leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leavediagname != null  and leavediagname != ''">and b.leavediagname like concat('%',
            #{leavediagname}, '%')
        </if>
        <if test="drname != null  and drname != ''">and b.drname like concat('%', #{drname}, '%')</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and b.leaveldeptname like concat('%',
            #{leaveldeptname}, '%')
        </if>
        <if test="deptname != null  and deptname != ''">and b.deptname like concat('%',
            #{deptname}, '%')
        </if>
        <if test="patid != null  and patid != ''">and a.id =#{patid}
        </if>
        <if test="leavehospitaldistrictname != null  and leavehospitaldistrictname != ''">and
            b.leavehospitaldistrictname like concat('%', #{leavehospitaldistrictname}, '%')
        </if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size()>0">
            AND b.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
        </if>
        <if test="hospitaldistrictcodeList != null and hospitaldistrictcodeList.size()>0">
            AND b.hospitaldistrictcode IN
            <foreach collection="hospitaldistrictcodeList" item="hospitaldistrictcode" open="(" separator=","
                     close=")">
                #{hospitaldistrictcode}
            </foreach>
        </if>
        <if test="leaveldeptcodes != null and leaveldeptcodes.size()>0">
            AND b.leaveldeptcode IN
            <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                     close=")">
                #{leaveldeptcode}
            </foreach>
        </if>
        <if test="deptcodeList != null and deptcodeList.size()>0">
            AND b.deptcode IN
            <foreach collection="deptcodeList" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="icd10codeList != null and icd10codeList.size()>0">
            AND b.leaveicd10code IN
            <foreach collection="icd10codeList" item="icd10code" open="(" separator=","
                     close=")">
                #{icd10code}
            </foreach>
        </if>
        <if test="medicalCode != null and medicalCode != ''">and b.medical_code = #{medicalCode}</if>
        <if test="medicalName != null and medicalName != ''">and b.medical_name = #{medicalName}</if>
        <if test="medicalCompany != null and medicalCompany != ''">and b.medical_company = #{medicalCompany}</if>
        GROUP BY
        b.inhospid,
        a.age,
        a.age_unit,
        a.telcode,
        a.id,
        a.sex
        <if test="cry != null and cry==1">
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==2">
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==3">
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==0">
            <!--
              åœ¨é™¢æ‚£è€…:可按入院床位正/倒序(bedSort=asc æˆ– desc),不传时保持默认排序(入院时间倒序 + åºŠä½æ­£åºï¼‰ã€‚
              åºŠä½æŒ‰è‡ªç„¶åºæŽ’(只用 5.7 ä¹Ÿæ”¯æŒçš„ REGEXP/CAST/TRIM/LENGTH,不依赖 8.0 çš„ REGEXP_REPLACE):
                1) åºŠä½ä¸ºç©º/空串的排最后
                2) çº¯æ•°å­—床位排前面,按数值大小(2 åœ¨ 10 å‰ï¼‰
                3) å¸¦å­—母/汉字的床位按长度 + å­—符串(A2 åœ¨ A12 å‰ï¼‰
                4) inhospid å…œåº•,保证翻页顺序稳定
              å€’序只反转 2)、3) ä¸¤ç»„内部顺序:空床位仍排最后,纯数字仍在前。
            -->
            <choose>
                <when test='"asc".equals(bedSort)'>
                    order by
                    coalesce(trim(b.bed_no), '') = '' ,
                    (trim(b.bed_no) regexp '^[0-9]+$') = 0 ,
                    cast(trim(b.bed_no) as unsigned) asc ,
                    length(trim(b.bed_no)) asc ,
                    trim(b.bed_no) asc ,
                    b.inhospid asc
                </when>
                <when test='"desc".equals(bedSort)'>
                    order by
                    coalesce(trim(b.bed_no), '') = '' ,
                    (trim(b.bed_no) regexp '^[0-9]+$') = 0 ,
                    cast(trim(b.bed_no) as unsigned) desc ,
                    length(trim(b.bed_no)) desc ,
                    trim(b.bed_no) desc ,
                    b.inhospid asc
                </when>
                <otherwise>
                    order by b.starttime desc , b.bed_no asc
                </otherwise>
            </choose>
        </if>
    </select>
    <select id="selectPatMedInhospListBySerialnum" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhosp"
            resultMap="PatMedInhospResult">
        select
        b.inhospid,
        b.nurse_id,
        b.medical_code,
        b.medical_name,
        b.medical_company,
        b.encounter_source_id,
        b.importance_flag_code,
        b.emergency_level_code,
        b.nurse_name,
        b.patname,
        b.management_doctor,
        b.management_doctor_code,
        b.out_way_id,
        b.campusid,
        b.out_way_name,
        b.deptcheck_flag,
        b.wardcheck_flag,
        b.in_deptcheck_flag,
        b.in_wardcheck_flag,
        b.diagcheck_flag,
        b.schedulecheck_flag,
        b.inhospstate,
        b.patno,
        b.inhospno,
        b.serialnum,
        b.hospitalname,
        b.hospitalcode,
        b.hospitaldistrictcode,
        b.hospitaldistrictname,
        b.icd10code,
        b.diagname,
        b.starttime,
        b.endtime,
        b.deptcode,
        b.deptname,
        b.roomno,
        b.bed_no,
        b.orgid,
        b.del_flag,
        b.update_by,
        b.update_time,
        b.create_by,
        b.create_time,
        b.isupload,
        b.upload_time,
        b.patid,
        b.leavediagname,
        b.leaveicd10code,
        b.drcode,
        b.drname,
        b.schemestatus,
        b.generalschemestatus,
        b.leaveldeptcode,
        b.leaveldeptname,
        b.hospitaldistrictid,
        b.leavehospitaldistrictcode,
        b.leavehospitaldistrictname,
        b.leavehospitaldistrictid,
        b.deptid,
        b.leaveldeptid,
        b.long_task_reason,
        b.schemetime,
        b.fuflag,
        b.fudate,
        b.fuperiod,
        b.futypecode,
        b.futypedesc,
        b.fuadvice,
        b.inhosp_myd_flag,
        b.fuspecialadvice
        FROM
        pat_med_inhosp b
        where 1=1
        <if test="inhospMydFlag != null ">and b.inhosp_myd_flag = #{inhospMydFlag}</if>
        <if test="orgid != null and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            and campusid = #{campusid}
        </if>
        <if test="encounterSourceId != null and encounterSourceId != ''">
            and b.encounter_source_id = #{encounterSourceId}
        </if>
        <if test="importanceFlagCode != null and importanceFlagCode != ''">
            and b.importance_flag_code = #{importanceFlagCode}
        </if>
        <if test="emergencyLevelCode != null and emergencyLevelCode != ''">
            and b.emergency_level_code = #{emergencyLevelCode}
        </if>
        <if test="serialnum != null and serialnum != ''">
            and b.serialnum = #{serialnum}
        </if>
        order by b.inhospid desc
    </select>
    <select id="selectPatMedInhospListByCondition" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhospVO"
            resultMap="PatMedInhospResult">
        select CONCAT( a.age, a.age_unit ) AS age,
        a.telcode as telcode,
        b.inhospid,
        b.medical_code,
        b.medical_name,
        b.medical_company,
        b.encounter_source_id,
        b.importance_flag_code,
        b.emergency_level_code,
        b.management_doctor,
        b.management_doctor_code,
        b.patname,
        b.operator_id,
        b.operator,
        b.campusid,
        b.remark,
        b.out_way_id,
        b.out_way_name,
        b.nurse_id,
        b.nurse_name,
        b.deptcheck_flag,
        b.wardcheck_flag,
        b.in_deptcheck_flag,
        b.in_wardcheck_flag,
        b.inhospstate,
        b.patno,
        b.inhospno,
        b.serialnum,
        b.hospitalname,
        b.hospitalcode,
        b.hospitaldistrictcode,
        b.hospitaldistrictname,
        b.icd10code,
        b.diagname,
        b.starttime,
        b.endtime,
        b.deptcode,
        b.deptname,
        b.roomno,
        b.bed_no,
        b.orgid,
        b.del_flag,
        b.update_by,
        b.update_time,
        b.create_by,
        b.create_time,
        b.isupload,
        b.upload_time,
        b.patid,
        b.leavediagname,
        b.leaveicd10code,
        b.drcode,
        b.drname,
        b.schemestatus,
        b.generalschemestatus,
        b.leaveldeptcode,
        b.leaveldeptname,
        b.hospitaldistrictid,
        b.leavehospitaldistrictcode,
        b.leavehospitaldistrictname,
        b.leavehospitaldistrictid,
        b.deptid,
        b.leaveldeptid,
        b.long_task_reason,
        b.fuflag,
        b.fudate,
        b.fuperiod,
        b.futypecode,
        b.futypedesc,
        b.fuadvice,
        b.fuspecialadvice,
        b.inhosp_myd_flag,
        b.schemetime
        FROM
        pat_med_inhosp b,
        pat_archive a
        where 1=1
        and a.del_flag=0
        and b.del_flag=0
        and a.id = b.patid
        AND a.idcardno != ''
        <if test="inhospMydFlag != null ">and b.inhosp_myd_flag = #{inhospMydFlag}</if>
        <if test="encounterSourceId != null and encounterSourceId != ''">
            and b.encounter_source_id = #{encounterSourceId}
        </if>
        <if test="importanceFlagCode != null and importanceFlagCode != ''">
            and b.importance_flag_code = #{importanceFlagCode}
        </if>
        <if test="emergencyLevelCode != null and emergencyLevelCode != ''">
            and b.emergency_level_code = #{emergencyLevelCode}
        </if>
        <if test="orgid != null and orgid != ''">
            and a.orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            and a.campusid = #{campusid}
        </if>
        <if test="hospitalname != null  and hospitalname != ''">and hospitalname like concat('%', #{hospitalname},
            '%')
        </if>
        <if test="managementDoctor != null ">and b.management_doctor = #{managementDoctor}</if>
        <if test="managementDoctorCode != null ">and b.management_doctor_code = #{managementDoctorCode}</if>
        <if test="patname != null  and patname != ''">and patname like concat('%', #{patname}, '%')</if>
        <if test="hospitaldistrictname != null  and hospitaldistrictname != ''">and hospitaldistrictname like
            concat('%', #{hospitaldistrictname}, '%')
        </if>
        <if test="endtime != null ">and b.endtime = #{endtime}</if>
        <if test="serialnum != null ">and b.serialnum = #{serialnum}</if>
        <if test="fuflag != null ">and b.fuflag = #{fuflag}</if>
        <if test="nurseId != null ">and b.nurse_id = #{nurseId}</if>
        <if test="nurseName != null ">and b.nurse_name = #{nurseName}</if>
        <if test="patno != null ">and b.patno = #{patno}</if>
        <if test="diagcheckFlag != null ">and b.diagcheck_flag = #{diagcheckFlag}</if>
        <if test="deptcheckFlag != null ">and b.deptcheck_flag = #{deptcheckFlag}</if>
        <if test="scheduleCheckFlag != null ">and b.schedulecheck_flag = #{scheduleCheckFlag}</if>
        <if test="wardcheckFlag != null ">and b.wardcheck_flag = #{wardcheckFlag}</if>
        <if test="inDeptcheckFlag != null ">and b.in_deptcheck_flag = #{inDeptcheckFlag}</if>
        <if test="inWardcheckFlag != null ">and b.in_wardcheck_flag = #{inWardcheckFlag}</if>
        <if test="inhospstate != null ">and b.inhospstate = #{inhospstate}</if>
        <if test="schemestatus != null ">and b.schemestatus = #{schemestatus}</if>
        <if test="leavediagname != null  and leavediagname != ''">and b.leavediagname like concat('%',
            #{leavediagname}, '%')
        </if>
        <if test="drname != null  and drname != ''">and b.drname like concat('%', #{drname}, '%')</if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size()>0">
            AND b.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
        </if>
        <if test="leaveldeptcodes != null and leaveldeptcodes.size()>0">
            AND b.leaveldeptcode IN
            <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator=","
                     close=")">
                #{leaveldeptcode}
            </foreach>
        </if>
        <if test="medicalCode != null  and medicalCode != ''">and b.medical_code like concat('%'#{medicalCode}, '%')
        </if>
        <if test="medicalName != null  and medicalName != ''">and b.medical_name like concat('%'#{medicalName}, '%')
        </if>
        <if test="medicalCompany != null  and medicalCompany != ''">and b.medical_company like
            concat('%'#{medicalCompany}, '%')
        </if>
        order by b.inhospid desc
    </select>
    <select id="getTagnameBypatid" parameterType="Long" resultType="string">
        SELECT GROUP_CONCAT(d.tagname, ', ')
        FROM pat_archivetag d
        WHERE d.patid = #{patid}
          AND d.del_flag = 0
    </select>
    <!-- æ‰¹é‡å–患者标签,替代逐行调用 getTagnameBypatid;每行含 patid、tagname ä¸¤ä¸ª key -->
    <select id="getTagnameByPatids" resultType="java.util.HashMap">
        SELECT d.patid                     AS patid,
               GROUP_CONCAT(d.tagname, ', ') AS tagname
        FROM pat_archivetag d
        WHERE d.del_flag = 0
          AND d.patid IN
        <foreach collection="patids" item="patid" open="(" separator="," close=")">
            #{patid}
        </foreach>
        GROUP BY d.patid
    </select>
    <select id="selectPatMedInhospByInhospid" parameterType="Long" resultMap="PatMedInhospResult">
        <include refid="selectPatMedInhospVo"/>
        where inhospid = #{inhospid}
    </select>
    <select id="selectPatMedInhosp" parameterType="Long" resultMap="PatMedInhospResult">
        <include refid="selectPatMedInhospVo"/>
        where del_flag=0
        <if test="cry != null and cry == 0 ">and inhospstate=0</if>
        <if test="cry != null and cry == 1 ">and inhospstate=1</if>
        <if test="cry != null and cry == 3 ">and inhospstate=3</if>
        <if test="inhospMydFlag != null ">and inhosp_myd_flag = #{inhospMydFlag}</if>
        <if test="serialnum != null">and serialnum = #{serialnum}</if>
        <if test="hospitalname != null">and hospitalname = #{hospitalname}</if>
        <if test="hospitalcode != null">and hospitalcode = #{hospitalcode}</if>
        <if test="hospitaldistrictcode != null">and hospitaldistrictcode = #{hospitaldistrictcode}</if>
        <if test="hospitaldistrictname != null">and hospitaldistrictname = #{hospitaldistrictname}</if>
        <if test="icd10code != null">and icd10code = #{icd10code}</if>
        <if test="diagname != null">and diagname = #{diagname}</if>
        <if test="starttime != null">and starttime = #{starttime}</if>
        <if test="endtime != null">and endtime = #{endtime}</if>
        <if test="deptcode != null">and deptcode = #{deptcode}</if>
        <if test="deptname != null">and deptname = #{deptname}</if>
        <if test="roomno != null">and roomno = #{roomno}</if>
        <if test="bedNo != null">and bed_no = #{bedNo}</if>
        <if test="orgid != null">and orgid = #{orgid}</if>
        <if test="campusid != null ">and campusid = #{campusid}</if>
        <if test="updateBy != null">and update_by = #{updateBy}</if>
        <if test="updateTime != null">and update_time = #{updateTime}</if>
        <if test="createBy != null">and create_by = #{createBy}</if>
        <if test="createTime != null">and create_time = #{createTime}</if>
        <if test="isupload != null">and isupload = #{isupload}</if>
        <if test="uploadTime != null">and upload_time = #{uploadTime}</if>
        <if test="patid != null">and patid = #{patid}</if>
        <if test="leavediagname != null">and leavediagname = #{leavediagname}</if>
        <if test="leaveicd10code != null">and leaveicd10code = #{leaveicd10code}</if>
        <if test="drcode != null">and drcode = #{drcode}</if>
        <if test="drname != null">and drname = #{drname}</if>
        <if test="schemestatus != null">and schemestatus = #{schemestatus}</if>
        <if test="generalschemestatus != null">and generalschemestatus = #{generalschemestatus}</if>
        <if test="leaveldeptcode != null">and leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leaveldeptname != null">and leaveldeptname = #{leaveldeptname}</if>
        <if test="hospitaldistrictid != null">and hospitaldistrictid = #{hospitaldistrictid}</if>
        <if test="leavehospitaldistrictcode != null">and leavehospitaldistrictcode = #{leavehospitaldistrictcode}</if>
        <if test="leavehospitaldistrictname != null">and leavehospitaldistrictname = #{leavehospitaldistrictname}</if>
        <if test="leavehospitaldistrictid != null">and leavehospitaldistrictid = #{leavehospitaldistrictid}</if>
        <if test="deptid != null">and deptid = #{deptid}</if>
        <if test="leaveldeptid != null">and leaveldeptid = #{leaveldeptid}</if>
        <if test="schemetime != null">and schemetime = #{schemetime}</if>
        <if test="patname!= null">and patname = #{patname}</if>
        <if test="patno != null">and patno = #{patno}</if>
        <if test="inhospstate != null">and inhospstate = #{inhospstate}</if>
        <if test="deptcheckFlag != null">and deptcheck_flag = #{deptcheckFlag}</if>
        <if test="wardcheckFlag != null">and wardcheck_flag = #{wardcheckFlag}</if>
        <if test="inDeptcheckFlag != null">and in_deptcheck_flag = #{inDeptcheckFlag}</if>
        <if test="inWardcheckFlag != null">and in_wardcheck_flag = #{inWardcheckFlag}</if>
        <if test="wardcheckFlag != null">and wardcheck_flag = #{wardcheckFlag}</if>
        <if test="diagcheckFlag != null">and diagcheck_flag = #{diagcheckFlag}</if>
        <if test="longTaskReason != null">and long_task_reason = #{longTaskReason}</if>
        <if test="nurseId != null ">and nurse_id = #{nurseId}</if>
        <if test="nurseName != null ">and nurse_name = #{nurseName}</if>
        <if test="outWayId != null ">and out_way_id = #{outWayId}</if>
        <if test="outWayName != null ">and out_way_name = #{outWayName}</if>
        <if test="guid != null ">and guid = #{guid}</if>
        <if test="operator != null ">and operator = #{operator}</if>
        <if test="operatorId != null ">and operator_id = #{operatorId}</if>
        <if test="remark != null ">and remark = #{remark}</if>
        <if test="fuflag != null ">and fuflag = #{fuflag}</if>
        <if test="fudate != null ">and fudate = #{fudate}</if>
        <if test="fuperiod != null ">and fuperiod = #{fuperiod}</if>
        <if test="futypecode != null ">and futypecode = #{futypecode}</if>
        <if test="futypedesc != null ">and futypedesc = #{futypedesc}</if>
        <if test="fuadvice != null ">and fuadvice = #{fuadvice}</if>
        <if test="fuspecialadvice != null ">and fuspecialadvice = #{fuspecialadvice}</if>
        <if test="managementDoctor != null ">and management_doctor = #{managementDoctor}</if>
        <if test="managementDoctorCode != null ">and management_doctor_code = #{managementDoctorCode}</if>
        <if test="importanceFlagCode != null ">and importance_flag_code = #{importanceFlagCode}</if>
        <if test="emergencyLevelCode != null ">and emergency_level_code = #{emergencyLevelCode}</if>
        <if test="encounterSourceId != null ">and encounter_source_id = #{encounterSourceId}</if>
        <if test="medicalCode != null  and medicalCode != ''">and medical_code like concat('%'#{medicalCode}, '%')</if>
        <if test="medicalName != null  and medicalName != ''">and medical_name like concat('%'#{medicalName}, '%')</if>
        <if test="medicalCompany != null  and medicalCompany != ''">and medical_company like
            concat('%'#{medicalCompany}, '%')
        </if>
    </select>
    <!-- æŒ‰æ‚£è€…编号 + å‡ºé™¢æ—¥æœŸæŸ¥è¯¢å‡ºé™¢è®°å½•(出院时间只比较到天,不比较时分秒),用于判断患者是否再入院 -->
    <select id="selectOutInhospByPatnoAndOutDate" resultMap="PatMedInhospResult">
        <include refid="selectPatMedInhospVo"/>
        where del_flag = 0
        and inhospstate = '1'
        <if test="patno != null and patno != ''">and patno = #{patno}</if>
        <if test="dayStart != null">and endtime &gt;= #{dayStart}</if>
        <if test="dayEnd != null">and endtime &lt; #{dayEnd}</if>
        order by inhospid desc
    </select>
    <insert id="insertPatMedInhosp" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhosp" useGeneratedKeys="true"
            keyProperty="inhospid">
        insert into pat_med_inhosp
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="serialnum != null">serialnum,</if>
            <if test="hospitalname != null">hospitalname,</if>
            <if test="hospitalcode != null">hospitalcode,</if>
            <if test="hospitaldistrictcode != null">hospitaldistrictcode,</if>
            <if test="hospitaldistrictname != null">hospitaldistrictname,</if>
            <if test="icd10code != null">icd10code,</if>
            <if test="diagname != null">diagname,</if>
            <if test="starttime != null">starttime,</if>
            <if test="endtime != null">endtime,</if>
            <if test="deptcode != null">deptcode,</if>
            <if test="deptname != null">deptname,</if>
            <if test="roomno != null">roomno,</if>
            <if test="bedNo != null">bed_no,</if>
            <if test="orgid != null">orgid,</if>
            <if test="campusid != null">campusid,</if>
            <if test="delFlag != null and delFlag != ''">del_flag,</if>
            <if test="updateBy != null">update_by,</if>
            <if test="updateTime != null">update_time,</if>
            <if test="createBy != null">create_by,</if>
            <if test="createTime != null">create_time,</if>
            <if test="isupload != null">isupload,</if>
            <if test="uploadTime != null">upload_time,</if>
            <if test="patid != null">patid,</if>
            <if test="leavediagname != null">leavediagname,</if>
            <if test="leaveicd10code != null">leaveicd10code,</if>
            <if test="drcode != null">drcode,</if>
            <if test="drname != null">drname,</if>
            <if test="schemestatus != null">schemestatus,</if>
            <if test="generalschemestatus != null">generalschemestatus,</if>
            <if test="leaveldeptcode != null">leaveldeptcode,</if>
            <if test="leaveldeptname != null">leaveldeptname,</if>
            <if test="hospitaldistrictid != null">hospitaldistrictid,</if>
            <if test="leavehospitaldistrictcode != null">leavehospitaldistrictcode,</if>
            <if test="leavehospitaldistrictname != null">leavehospitaldistrictname,</if>
            <if test="leavehospitaldistrictid != null">leavehospitaldistrictid,</if>
            <if test="deptid != null">deptid,</if>
            <if test="leaveldeptid != null">leaveldeptid,</if>
            <if test="schemetime != null">schemetime,</if>
            <if test="patname!= null">patname,</if>
            <if test="patno != null">patno,</if>
            <if test="inhospstate != null">inhospstate,</if>
            <if test="deptcheckFlag != null">deptcheck_flag,</if>
            <if test="wardcheckFlag != null">wardcheck_flag,</if>
            <if test="inDeptcheckFlag != null">in_deptcheck_flag,</if>
            <if test="inWardcheckFlag != null">in_wardcheck_flag,</if>
            <if test="diagcheckFlag != null">diagcheck_flag,</if>
            <if test="longTaskReason != null">long_task_reason,</if>
            <if test="nurseId != null ">nurse_id,</if>
            <if test="nurseName != null ">nurse_name,</if>
            <if test="outWayId != null ">out_way_id,</if>
            <if test="outWayName != null ">out_way_name,</if>
            <if test="guid != null ">guid,</if>
            <if test="operator != null ">operator,</if>
            <if test="operatorId != null ">operator_id,</if>
            <if test="inhospno != null ">inhospno,</if>
            <if test="remark != null ">remark,</if>
            <if test="fuflag != null ">fuflag,</if>
            <if test="fudate != null ">fudate,</if>
            <if test="fuperiod != null ">fuperiod,</if>
            <if test="futypecode != null ">futypecode,</if>
            <if test="futypedesc != null ">futypedesc,</if>
            <if test="fuadvice != null ">fuadvice,</if>
            <if test="fuspecialadvice != null ">fuspecialadvice,</if>
            <if test="managementDoctor != null ">management_doctor,</if>
            <if test="managementDoctorCode != null ">management_doctor_code,</if>
            <if test="inhospMydFlag != null ">inhosp_myd_flag,</if>
            <if test="importanceFlagCode != null ">importance_flag_code,</if>
            <if test="emergencyLevelCode != null ">emergency_level_code,</if>
            <if test="encounterSourceId != null ">encounter_source_id,</if>
            <if test="medicalCode != null  and medicalCode != ''">medical_code,</if>
            <if test="medicalName != null  and medicalName != ''">medical_name,</if>
            <if test="medicalCompany != null  and medicalCompany != ''">medical_company,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="serialnum != null">#{serialnum},</if>
            <if test="hospitalname != null">#{hospitalname},</if>
            <if test="hospitalcode != null">#{hospitalcode},</if>
            <if test="hospitaldistrictcode != null">#{hospitaldistrictcode},</if>
            <if test="hospitaldistrictname != null">#{hospitaldistrictname},</if>
            <if test="icd10code != null">#{icd10code},</if>
            <if test="diagname != null">#{diagname},</if>
            <if test="starttime != null">#{starttime},</if>
            <if test="endtime != null">#{endtime},</if>
            <if test="deptcode != null">#{deptcode},</if>
            <if test="deptname != null">#{deptname},</if>
            <if test="roomno != null">#{roomno},</if>
            <!-- åºŠä½ç©ºä¸²ï¼ˆHIS æ— å½“前床位)落库为 null,保持"有值 or null"两种状态 -->
            <if test="bedNo != null">nullif(#{bedNo}, ''),</if>
            <if test="orgid != null">#{orgid},</if>
            <if test="campusid != null">#{campusid},</if>
            <if test="delFlag != null and delFlag != ''">#{delFlag},</if>
            <if test="updateBy != null">#{updateBy},</if>
            <if test="updateTime != null">#{updateTime},</if>
            <if test="createBy != null">#{createBy},</if>
            <if test="createTime != null">#{createTime},</if>
            <if test="isupload != null">#{isupload},</if>
            <if test="uploadTime != null">#{uploadTime},</if>
            <if test="patid != null">#{patid},</if>
            <if test="leavediagname != null">#{leavediagname},</if>
            <if test="leaveicd10code != null">#{leaveicd10code},</if>
            <if test="drcode != null">#{drcode},</if>
            <if test="drname != null">#{drname},</if>
            <if test="schemestatus != null">#{schemestatus},</if>
            <if test="generalschemestatus != null">#{generalschemestatus},</if>
            <if test="leaveldeptcode != null">#{leaveldeptcode},</if>
            <if test="leaveldeptname != null">#{leaveldeptname},</if>
            <if test="hospitaldistrictid != null">#{hospitaldistrictid},</if>
            <if test="leavehospitaldistrictcode != null">#{leavehospitaldistrictcode},</if>
            <if test="leavehospitaldistrictname != null">#{leavehospitaldistrictname},</if>
            <if test="leavehospitaldistrictid != null">#{leavehospitaldistrictid},</if>
            <if test="deptid != null">#{deptid},</if>
            <if test="leaveldeptid != null">#{leaveldeptid},</if>
            <if test="schemetime != null">#{schemetime},</if>
            <if test="patname!= null">#{patname},</if>
            <if test="patno != null">#{patno},</if>
            <if test="inhospstate != null">#{inhospstate},</if>
            <if test="deptcheckFlag != null">#{deptcheckFlag},</if>
            <if test="wardcheckFlag != null">#{wardcheckFlag},</if>
            <if test="inDeptcheckFlag != null">#{inDeptcheckFlag},</if>
            <if test="inWardcheckFlag != null">#{inWardcheckFlag},</if>
            <if test="diagcheckFlag != null">#{diagcheckFlag},</if>
            <if test="longTaskReason != null">#{longTaskReason},</if>
            <if test="nurseId != null ">#{nurseId},</if>
            <if test="nurseName != null ">#{nurseName},</if>
            <if test="outWayId != null ">#{outWayId},</if>
            <if test="outWayName != null ">#{outWayName},</if>
            <if test="guid != null ">#{guid},</if>
            <if test="operator != null ">#{operator},</if>
            <if test="operatorId != null ">#{operatorId},</if>
            <if test="inhospno != null ">#{inhospno},</if>
            <if test="remark != null ">#{remark},</if>
            <if test="fuflag != null ">#{fuflag},</if>
            <if test="fudate != null ">#{fudate},</if>
            <if test="fuperiod != null ">#{fuperiod},</if>
            <if test="futypecode != null ">#{futypecode},</if>
            <if test="futypedesc != null ">#{futypedesc},</if>
            <if test="fuadvice != null ">#{fuadvice},</if>
            <if test="fuspecialadvice != null ">#{fuspecialadvice},</if>
            <if test="managementDoctor != null ">#{managementDoctor},</if>
            <if test="managementDoctorCode != null ">#{managementDoctorCode},</if>
            <if test="inhospMydFlag != null ">#{inhospMydFlag},</if>
            <if test="importanceFlagCode != null ">#{importanceFlagCode},</if>
            <if test="emergencyLevelCode != null ">#{emergencyLevelCode},</if>
            <if test="encounterSourceId != null ">#{encounterSourceId},</if>
            <if test="medicalCode != null  and medicalCode != ''">#{medicalCode},</if>
            <if test="medicalName != null  and medicalName != ''">#{medicalName},</if>
            <if test="medicalCompany != null  and medicalCompany != ''">#{medicalCompany},</if>
        </trim>
    </insert>
    <insert id="insertPatMedInhospBatch" parameterType="java.util.List" useGeneratedKeys="true"
            keyProperty="item.inhospid">
        insert into pat_med_inhosp (
        serialnum, hospitalname, hospitalcode, hospitaldistrictcode, hospitaldistrictname,
        icd10code, diagname, starttime, endtime, deptcode, deptname, roomno, bed_no,
        orgid, del_flag, update_by, update_time, create_by, create_time, isupload,
        upload_time, patid, leavediagname, leaveicd10code, drcode, drname,
        schemestatus, generalschemestatus, leaveldeptcode, leaveldeptname, hospitaldistrictid,
        leavehospitaldistrictcode, leavehospitaldistrictname, leavehospitaldistrictid,
        deptid, leaveldeptid, schemetime, patname, patno, inhospstate,
        deptcheck_flag, wardcheck_flag,in_deptcheck_flag, in_wardcheck_flag, diagcheck_flag, long_task_reason,
        nurse_id, nurse_name, out_way_id, out_way_name, guid,
        operator, operator_id, inhospno, remark,
        fuflag, fudate, fuperiod, futypecode, futypedesc, fuadvice, fuspecialadvice, management_doctor,
        management_doctor_code,campusid, inhosp_myd_flag, importance_flag_code, emergency_level_code, encounter_source_id,
        medical_code, medical_name, medical_company
        )
        values
        <foreach collection="list" item="item" separator=",">
            (
            #{item.serialnum}, #{item.hospitalname}, #{item.hospitalcode}, #{item.hospitaldistrictcode},
            #{item.hospitaldistrictname},
            #{item.icd10code}, #{item.diagname}, #{item.starttime}, #{item.endtime}, #{item.deptcode}, #{item.deptname},
            #{item.roomno}, nullif(#{item.bedNo}, ''),
            #{item.orgid}, #{item.delFlag}, #{item.updateBy}, #{item.updateTime}, #{item.createBy}, #{item.createTime},
            #{item.isupload},
            #{item.uploadTime}, #{item.patid}, #{item.leavediagname}, #{item.leaveicd10code}, #{item.drcode},
            #{item.drname},
            #{item.schemestatus}, #{item.generalschemestatus}, #{item.leaveldeptcode}, #{item.leaveldeptname},
            #{item.hospitaldistrictid},
            #{item.leavehospitaldistrictcode}, #{item.leavehospitaldistrictname}, #{item.leavehospitaldistrictid},
            #{item.deptid}, #{item.leaveldeptid}, #{item.schemetime}, #{item.patname}, #{item.patno},
            #{item.inhospstate},
            #{item.deptcheckFlag}, #{item.wardcheckFlag},#{item.inDeptcheckFlag}, #{item.inWardcheckFlag}, #{item.diagcheckFlag}, #{item.longTaskReason},
            #{item.nurseId}, #{item.nurseName}, #{item.outWayId}, #{item.outWayName}, #{item.guid},
            #{item.operator}, #{item.operatorId}, #{item.inhospno}, #{item.remark},
            #{item.fuflag}, #{item.fudate}, #{item.fuperiod}, #{item.futypecode}, #{item.futypedesc}, #{item.fuadvice},
            #{item.fuspecialadvice}, #{item.managementDoctor}, #{item.managementDoctorCode}, #{item.campusid}, #{item.inhospMydFlag},
            #{item.importanceFlagCode}, #{item.emergencyLevelCode}, #{item.encounterSourceId},
            #{item.medicalCode}, #{item.medicalName}, #{item.medicalCompany}
            )
        </foreach>
    </insert>
    <update id="updatePatMedInhosp" parameterType="com.ruoyi.inhospvisit.domain.PatMedInhosp">
        update pat_med_inhosp
        <trim prefix="SET" suffixOverrides=",">
            <!--
              å…œåº•:下面字段全为空时(例如调用方只传了 inhospid),如果没有任何 SET é¡¹ä¼šç”Ÿæˆ
              "update pat_med_inhosp where inhospid = ?" è¿™ç§éžæ³• SQL。这里放一个自赋值,保证 SET ä¸ä¸ºç©ºä¸”不改数据。
            -->
            inhospid = inhospid,
            <if test="serialnum != null">serialnum = #{serialnum},</if>
            <if test="hospitalname != null">hospitalname = #{hospitalname},</if>
            <if test="hospitalcode != null">hospitalcode = #{hospitalcode},</if>
            <if test="hospitaldistrictcode != null">hospitaldistrictcode = #{hospitaldistrictcode},</if>
            <if test="hospitaldistrictname != null">hospitaldistrictname = #{hospitaldistrictname},</if>
            <if test="icd10code != null">icd10code = #{icd10code},</if>
            <if test="diagname != null">diagname = #{diagname},</if>
            <if test="starttime != null">starttime = #{starttime},</if>
            <if test="endtime != null">endtime = #{endtime},</if>
            <if test="deptcode != null">deptcode = #{deptcode},</if>
            <if test="deptname != null">deptname = #{deptname},</if>
            <if test="roomno != null">roomno = #{roomno},</if>
            <!--
              åºŠä½ï¼šHIS ä¼ ç©ºä¸²ä»£è¡¨"当前没有床位"(换床、或人已离院还没办出院手续),落库为 null;
              å®Œå…¨ä¸ä¼ (null)则不改动床位。空串与 null å¿…须分开判断,不能都跳过,否则床位不会被清掉。
            -->
            <if test='bedNo != null and !"".equals(bedNo)'>bed_no = #{bedNo},</if>
            <if test='bedNo != null and "".equals(bedNo)'>bed_no = null,</if>
            <if test="orgid != null">orgid = #{orgid},</if>
            <if test="campusid != null">campusid = #{campusid},</if>
            <if test="delFlag != null and delFlag != ''">del_flag = #{delFlag},</if>
            <if test="updateBy != null">update_by = #{updateBy},</if>
            <if test="updateTime != null">update_time = #{updateTime},</if>
            <if test="createBy != null">create_by = #{createBy},</if>
            <if test="createTime != null">create_time = #{createTime},</if>
            <if test="isupload != null">isupload = #{isupload},</if>
            <if test="uploadTime != null">upload_time = #{uploadTime},</if>
            <if test="patid != null">patid = #{patid},</if>
            <if test="leavediagname != null">leavediagname = #{leavediagname},</if>
            <if test="leaveicd10code != null">leaveicd10code = #{leaveicd10code},</if>
            <if test="drcode != null">drcode = #{drcode},</if>
            <if test="drname != null">drname = #{drname},</if>
            <if test="schemestatus != null">schemestatus = #{schemestatus},</if>
            <if test="generalschemestatus != null">generalschemestatus = #{generalschemestatus},</if>
            <if test="leaveldeptcode != null">leaveldeptcode = #{leaveldeptcode},</if>
            <if test="leaveldeptname != null">leaveldeptname = #{leaveldeptname},</if>
            <if test="hospitaldistrictid != null">hospitaldistrictid = #{hospitaldistrictid},</if>
            <if test="leavehospitaldistrictcode != null">leavehospitaldistrictcode = #{leavehospitaldistrictcode},</if>
            <if test="leavehospitaldistrictname != null">leavehospitaldistrictname = #{leavehospitaldistrictname},</if>
            <if test="leavehospitaldistrictid != null">leavehospitaldistrictid = #{leavehospitaldistrictid},</if>
            <if test="deptid != null">deptid = #{deptid},</if>
            <if test="leaveldeptid != null">leaveldeptid = #{leaveldeptid},</if>
            <if test="schemetime != null">schemetime = #{schemetime},</if>
            <if test="patname!= null">patname = #{patname},</if>
            <if test="patno != null">patno = #{patno},</if>
            <if test="inhospstate != null">inhospstate = #{inhospstate},</if>
            <if test="deptcheckFlag != null">deptcheck_flag = #{deptcheckFlag},</if>
            <if test="scheduleCheckFlag != null">schedulecheck_flag = #{scheduleCheckFlag},</if>
            <if test="wardcheckFlag != null">wardcheck_flag = #{wardcheckFlag},</if>
            <if test="inDeptcheckFlag != null">in_deptcheck_flag = #{inDeptcheckFlag},</if>
            <if test="inWardcheckFlag != null">in_wardcheck_flag = #{inWardcheckFlag},</if>
            <if test="diagcheckFlag != null">diagcheck_flag = #{diagcheckFlag},</if>
            <if test="longTaskReason != null">long_task_reason = #{longTaskReason},</if>
            <if test="nurseId != null ">nurse_id = #{nurseId},</if>
            <if test="nurseName != null ">nurse_name = #{nurseName},</if>
            <if test="outWayId != null ">out_way_id = #{outWayId},</if>
            <if test="outWayName != null ">out_way_name = #{outWayName},</if>
            <if test="guid != null ">guid = #{guid},</if>
            <if test="operator != null ">operator = #{operator},</if>
            <if test="operatorId != null ">operator_id = #{operatorId},</if>
            <if test="remark != null ">remark = #{remark},</if>
            <if test="fuflag != null ">fuflag = #{fuflag},</if>
            <if test="fudate != null ">fudate = #{fudate},</if>
            <if test="fuperiod != null ">fuperiod = #{fuperiod},</if>
            <if test="futypecode != null ">futypecode = #{futypecode},</if>
            <if test="futypedesc != null ">futypedesc = #{futypedesc},</if>
            <if test="fuadvice != null ">fuadvice = #{fuadvice},</if>
            <if test="fuspecialadvice != null ">fuspecialadvice = #{fuspecialadvice},</if>
            <if test="managementDoctor != null ">management_doctor = #{managementDoctor},</if>
            <if test="managementDoctorCode != null ">management_doctor_code = #{managementDoctorCode},</if>
            <if test="inhospMydFlag != null ">inhosp_myd_flag = #{inhospMydFlag},</if>
            <if test="importanceFlagCode != null ">importance_flag_code = #{importanceFlagCode},</if>
            <if test="emergencyLevelCode != null ">emergency_level_code = #{emergencyLevelCode},</if>
            <if test="encounterSourceId != null ">encounter_source_id = #{encounterSourceId},</if>
            <if test="medicalCode != null  and medicalCode != ''">medical_code = #{medicalCode},</if>
            <if test="medicalName != null  and medicalName != ''">medical_name = #{medicalName},</if>
            <if test="medicalCompany != null  and medicalCompany != ''">medical_company = #{medicalCompany},</if>
        </trim>
        where inhospid = #{inhospid}
    </update>
    <delete id="deletePatMedInhospByInhospid" parameterType="Long">
        update pat_med_inhosp
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where inhospid = #{inhospid}
    </delete>
    <delete id="deletePatMedInhospByInhospno" parameterType="String">
        update pat_med_inhosp
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where inhospno = #{inhospno}
    </delete>
    <delete id="deletePatMedInhospByInhospids" parameterType="String">
        delete from pat_med_inhosp where inhospid in
        <foreach item="inhospid" collection="array" open="(" separator="," close=")">
            #{inhospid}
        </foreach>
    </delete>
    <select id="selectPatMedInhospCount" parameterType="com.ruoyi.common.core.service.dto.PatMedReq"
            resultType="com.ruoyi.common.core.service.dto.PatMedRes">
        SELECT SUM(rc) AS rc,
        SUM(rs) AS rs,
        SUM(scsf) AS scsf,
        SUM(zcsf) AS zcsf,
        SUM(zbsf) AS zbsf
        FROM (
        <!-- å‡ºé™¢äººæ¬¡ï¼šåŽ»æŽ‰ date_format åŒ…装,使日期字段索引生效 -->
        SELECT
        COUNT(1) AS rc,
        0 AS rs,
        0 AS scsf,
        0 AS zcsf,
        0 AS zbsf
        FROM pat_med_inhosp
        JOIN pat_archive ON pat_archive.id = pat_med_inhosp.patid
        WHERE pat_med_inhosp.del_flag = 0
        <if test="orgid != null and orgid != ''">
            AND pat_med_inhosp.orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            and pat_med_inhosp.campusid = #{campusid}
        </if>
        AND pat_archive.idcardno IS NOT NULL
        <if test="zy != null">
            AND pat_med_inhosp.inhospstate = 0
            AND pat_med_inhosp.starttime &gt;= #{startDate}
            AND pat_med_inhosp.starttime &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY)
            <if test="deptcodeList != null and deptcodeList.size() > 0">
                AND pat_med_inhosp.deptcode IN
                <foreach collection="deptcodeList" item="deptcode" open="(" separator="," close=")">
                    #{deptcode}
                </foreach>
            </if>
        </if>
        <if test="cy != null">
            AND pat_med_inhosp.inhospstate = 1
            AND pat_med_inhosp.endtime &gt;= #{startDate}
            AND pat_med_inhosp.endtime &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY)
            <if test="deptcodeList != null and deptcodeList.size() > 0">
                AND pat_med_inhosp.leaveldeptcode IN
                <foreach collection="deptcodeList" item="deptcode" open="(" separator="," close=")">
                    #{deptcode}
                </foreach>
            </if>
        </if>
        UNION ALL
        <!-- éšè®¿æœåŠ¡/首次/再次/专病随访:合并为单次扫描,用条件聚合代替4个 UNION ALL -->
        SELECT
        0 AS rc,
        COUNT(1) AS rs,
        SUM(CASE WHEN service_type = 2 AND visit_count = 1 THEN 1 ELSE 0 END) AS scsf,
        SUM(CASE WHEN service_type = 2 AND visit_count > 1 THEN 1 ELSE 0 END) AS zcsf,
        SUM(CASE WHEN service_type = 13 THEN 1 ELSE 0 END) AS zbsf
        FROM service_subtask
        WHERE del_flag = 0
        AND service_type IN (2, 13)
        <if test="orgid != null and orgid != ''">
            AND orgid = #{orgid}
        </if>
        <if test="campusid != null and campusid != ''">
            and campusid = #{campusid}
        </if>
        <if test="startDate != null">
            AND visit_time &gt;= #{startDate}
        </if>
        <if test="endDate != null">
            AND visit_time &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY)
        </if>
        <if test="deptcodeList != null and deptcodeList.size() > 0">
            AND deptcode IN
            <foreach collection="deptcodeList" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
        </if>
        ) AS combined_data
    </select>
    <!-- æŸ¥è¯¢ä¸‰ä¸ªç»´åº¦ä»»æ„ä¸€ä¸ªæœªå¤„理的出院患者 -->
    <select id="selectNeedProcessList" resultMap="PatMedInhospResult">
        SELECT *
        FROM pat_med_inhosp
        WHERE inhospstate = '1'
          AND fuflag = '1'
          AND del_flag = '0'
          AND (
                    dept_check_flag = '0'
                OR ward_check_flag = '0'
                OR diag_check_flag = '0'
            )
    </select>
</mapper>
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitArchiveMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inhospvisit.mapper.VisitArchiveMapper">
    <!--
      ============================================================================
      å‡ºå…¥é™¢éšè®¿æ¨¡å—自带的 pat_archive åªè¯»æŸ¥è¯¢ã€‚
      ============================================================================
      ä¸Ž smartor çš„ mapper/smartor/PatArchiveMapper.xml çš„ selectPatArchiveByPatid ç­‰ä»·ã€‚
      åŽŸå®žçŽ°ï¼ˆPatArchiveMapper.xml:272-275):
          <include refid="selectPatArchiveVo"/>
          where id = #{id} and del_flag=0
      âš ï¸ å¿…须保留的原语义(写错会直接改变分支走向):
         Â· where æ¡ä»¶æ˜¯ id = ? ã€and del_flag = 0】—— å·²åˆ é™¤çš„æ¡£æ¡ˆä¼šè¢«åˆ¤ä¸º"查不到",
           ä»Žè€Œèµ° {"患者基本信息为空"} åˆ†æ”¯ã€‚早期草稿曾以为没有 del_flag è¿‡æ»¤ï¼Œå·²ä¿®æ­£ã€‚
      âš ï¸ ä¸ŽåŽŸå®žçŽ°çš„ã€å”¯ä¸€å·®å¼‚ã€‘ï¼šåª SELECT éšè®¿ä¾§çœŸæ­£è¯»åˆ°çš„列(原实现是整份列清单,70+ åˆ—)。
         éšè®¿ç»„装对 PatArchive çš„ getter è®¿é—®é›†åˆæ°å¥½æ˜¯è¿™ 9 ä¸ªï¼š
             age id idcardno name notrequiredFlag notrequiredreason relativetelcode sex telcode
         è¿™é‡Œå¤šå¸¦ createTime ä»…用于排查,不影响任何分支。
      ============================================================================
    -->
    <resultMap type="com.ruoyi.common.core.service.dto.VisitArchiveRow" id="VisitArchiveRowResult">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="idcardno" column="idcardno"/>
        <result property="telcode" column="telcode"/>
        <result property="relativetelcode" column="relativetelcode"/>
        <result property="sex" column="sex"/>
        <result property="age" column="age"/>
        <result property="birthdate" column="birthdate"/>
        <result property="notrequiredFlag" column="notrequired_flag"/>
        <result property="notrequiredreason" column="notrequiredreason"/>
        <result property="createTime" column="create_time"/>
    </resultMap>
    <select id="selectByPatid" parameterType="Long" resultMap="VisitArchiveRowResult">
        select id,
               name,
               idcardno,
               telcode,
               relativetelcode,
               sex,
               age,
               birthdate,
               notrequired_flag,
               notrequiredreason,
               create_time
        from pat_archive
        where id = #{patid} and del_flag = 0
    </select>
</mapper>
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitInhospMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,385 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inhospvisit.mapper.VisitInhospMapper">
    <!--
      ============================================================================
      å‡ºå…¥é™¢éšè®¿æ¨¡å—自带的 pat_med_inhosp æŸ¥è¯¢ã€‚
      ============================================================================
      ä¸Ž smartor çš„ mapper/smartor/PatMedInhospMapper.xml æ˜¯ã€ä¸¤å¥—等价的 SQL】:
        Â· æœ¬æ¨¡å—只依赖 ruoyi-common(不依赖 smartor,否则循环依赖),所以自己查;
        Â· çºªå¾‹ï¼šæ”¹ä»»ä¸€è¾¹å¿…须同步改另一边,并重跑
            sql/_probe/verify_inhospvisit_mapper.py          ï¼ˆæŽ¥å£ â†” XML ä¸€ä¸€å¯¹åº”)
            sql/_probe/verify_inhospvisit_sql_expansion.py   ï¼ˆå±•开后逐条比对 WHERE/GROUP BY/ORDER BY)
      ä¸ŽåŽŸ XML çš„【三处有意差异】(都已在探针里登记):
        1) ä¸å¼•入原文件的 <sql id="selectPatMedInhospVo"> å…±äº«ç‰‡æ®µ â€”— æœ¬æ–‡ä»¶è‡ªå¸¦åˆ—清单,
           è‡ªåŒ…含、便于逐条比对(满意度那次踩过"片段两处维护"的坑,这里选择相反做法);
        2) selectForScan çš„ SELECT åˆ—做了删减:只保留随访侧真正用到的列
           ï¼ˆåŽŸ XML æœ‰ 70+ åˆ—,本模块只需要 40 åˆ—)。删列不影响 WHERE/GROUP BY/ORDER BY è¯­ä¹‰ï¼›
        3) ç»“果列用 AS åˆ«åç»Ÿä¸€æˆè¡Œå¯¹è±¡å±žæ€§åï¼Œå› æ­¤åªéœ€è¦ä¸€ä¸ª resultMap。
      å¿…须【逐字保留】的原语义(写错就直接改变线上随访结果):
        1) JOIN pat_archive ä¸” a.del_flag=0 / b.del_flag=0 / a.idcardno IS NOT NULL
           â€”— å†³å®š"谁会被扫到";
        2) GROUP BY b.inhospid, a.age, a.age_unit, a.telcode, a.id, a.sex
           â€”— åŽ»é‡è¯­ä¹‰ï¼›
        3) ä¸‰ç§æŽ’序(cry=1/2/3 æŒ‰å‡ºé™¢æ—¶é—´å€’序;cry=0 æŒ‰åºŠä½è‡ªç„¶åºä¸‰é€‰ä¸€ï¼‰
           â€”— å†³å®šã€å¤„理次序】,而次序会影响"再入院停机"的最终结果。
      ============================================================================
    -->
    <resultMap type="com.ruoyi.inhospvisit.domain.row.VisitInhospRow" id="VisitInhospRowResult">
        <result property="inhospid" column="inhospid"/>
        <result property="patid" column="patid"/>
        <result property="orgid" column="orgid"/>
        <result property="campusid" column="campusid"/>
        <result property="patno" column="patno"/>
        <result property="inhospno" column="inhospno"/>
        <result property="serialnum" column="serialnum"/>
        <result property="patname" column="patname"/>
        <result property="age" column="age"/>
        <result property="sex" column="sex"/>
        <result property="telcode" column="telcode"/>
        <result property="starttime" column="starttime"/>
        <result property="endtime" column="endtime"/>
        <result property="fudate" column="fudate"/>
        <result property="deptcode" column="deptcode"/>
        <result property="deptname" column="deptname"/>
        <result property="hospitaldistrictcode" column="hospitaldistrictcode"/>
        <result property="hospitaldistrictname" column="hospitaldistrictname"/>
        <result property="leaveldeptcode" column="leaveldeptcode"/>
        <result property="leaveldeptname" column="leaveldeptname"/>
        <result property="leavehospitaldistrictcode" column="leavehospitaldistrictcode"/>
        <result property="leavehospitaldistrictname" column="leavehospitaldistrictname"/>
        <result property="leaveicd10code" column="leaveicd10code"/>
        <result property="leavediagname" column="leavediagname"/>
        <result property="diagname" column="diagname"/>
        <result property="nurseId" column="nurse_id"/>
        <result property="nurseName" column="nurse_name"/>
        <result property="drcode" column="drcode"/>
        <result property="drname" column="drname"/>
        <result property="managementDoctor" column="management_doctor"/>
        <result property="managementDoctorCode" column="management_doctor_code"/>
        <result property="deptcheckFlag" column="deptcheck_flag"/>
        <result property="wardcheckFlag" column="wardcheck_flag"/>
        <result property="diagcheckFlag" column="diagcheck_flag"/>
        <result property="scheduleCheckFlag" column="schedulecheck_flag"/>
        <result property="inDeptcheckFlag" column="in_deptcheck_flag"/>
        <result property="inWardcheckFlag" column="in_wardcheck_flag"/>
        <result property="inhospstate" column="inhospstate"/>
        <result property="fuflag" column="fuflag"/>
        <result property="longTaskReason" column="long_task_reason"/>
        <result property="remark" column="remark"/>
        <result property="medicalCode" column="medical_code"/>
    </resultMap>
    <!--
      éšè®¿æ‰«æ / æŒ‰æ ‡å¿—位过滤(对应原 PatMedInhospMapper.xml çš„ selectPatMedInhospList,L166-406)。
      æ¡ä»¶å—的顺序与内容逐条对齐原 XML;只保留随访侧会用到的条件,删掉纯展示类条件
      ï¼ˆhospitalname / bedNo / outWayId / importanceFlagCode ç­‰ï¼‰ï¼Œå·²ç™»è®°ä¸ºæœ‰æ„å·®å¼‚。
    -->
    <select id="selectForScan" parameterType="com.ruoyi.inhospvisit.domain.row.VisitInhospRow"
            resultMap="VisitInhospRowResult">
        select
        a.age AS age,
        a.telcode as telcode,
        a.sex,
        b.inhospid,
        b.nurse_id,
        b.nurse_name,
        b.patname,
        b.management_doctor,
        b.management_doctor_code,
        b.deptcheck_flag,
        b.wardcheck_flag,
        b.in_deptcheck_flag,
        b.in_wardcheck_flag,
        b.diagcheck_flag,
        b.schedulecheck_flag,
        b.inhospstate,
        b.patno,
        b.inhospno,
        b.serialnum,
        b.hospitaldistrictcode,
        b.hospitaldistrictname,
        b.diagname,
        b.starttime,
        b.endtime,
        b.deptcode,
        b.campusid,
        b.deptname,
        b.orgid,
        b.patid,
        b.leavediagname,
        b.leaveicd10code,
        b.drcode,
        b.drname,
        b.leaveldeptcode,
        b.leaveldeptname,
        b.leavehospitaldistrictcode,
        b.leavehospitaldistrictname,
        b.long_task_reason,
        b.fuflag,
        b.fudate,
        b.remark,
        b.medical_code
        FROM
        pat_med_inhosp b
        JOIN
        pat_archive a ON a.id = b.patid
        where 1=1
        AND a.del_flag = 0
        AND b.del_flag = 0
        AND a.idcardno IS NOT NULL
        <if test="inhospid != null">and b.inhospid = #{inhospid}</if>
        <if test="startOutHospTime != null ">and date_format(b.endtime,'%y%m%d') &gt;=
            date_format(#{startOutHospTime},'%y%m%d')
        </if>
        <if test="endOutHospTime != null ">and date_format(b.endtime,'%y%m%d') &lt;=
            date_format(#{endOutHospTime},'%y%m%d')
        </if>
        <if test="startInHospTime != null ">and date_format(b.starttime,'%y%m%d') &gt;=
            date_format(#{startInHospTime},'%y%m%d')
        </if>
        <if test="endInHospTime != null ">and date_format(b.starttime,'%y%m%d') &lt;=
            date_format(#{endInHospTime},'%y%m%d')
        </if>
        <if test="patname != null  and patname != ''">and patname like concat('%', #{patname}, '%')</if>
        <if test="endtime != null ">and b.endtime = #{endtime}</if>
        <if test="fuflag != null ">and b.fuflag = #{fuflag}</if>
        <if test="orgid != null ">and b.orgid = #{orgid}</if>
        <if test="campusid != null ">and b.campusid = #{campusid}</if>
        <if test="serialnum != null ">and b.serialnum = #{serialnum}</if>
        <if test="patno != null ">and b.patno = #{patno}</if>
        <if test="nurseId != null ">and b.nurse_id = #{nurseId}</if>
        <if test="nurseName != null and nurseName != ''">and b.nurse_name = #{nurseName}</if>
        <if test="deptcheckFlag != null ">and b.deptcheck_flag = #{deptcheckFlag}</if>
        <if test="scheduleCheckFlag != null ">and b.schedulecheck_flag = #{scheduleCheckFlag}</if>
        <if test="wardcheckFlag != null ">and b.wardcheck_flag = #{wardcheckFlag}</if>
        <if test="inDeptcheckFlag != null ">and b.in_deptcheck_flag = #{inDeptcheckFlag}</if>
        <if test="inWardcheckFlag != null ">and b.in_wardcheck_flag = #{inWardcheckFlag}</if>
        <if test="diagcheckFlag != null ">and b.diagcheck_flag = #{diagcheckFlag}</if>
        <!--
          âš ï¸ ä»¥ä¸‹è¿™äº›æ¡ä»¶åœ¨ã€éšè®¿ç”Ÿæˆé“¾ã€‘上确实不可达(13 ä¸ªè°ƒç”¨ç‚¹é‡Œæ²¡æœ‰ä¸€ä¸ªè®¾å®ƒä»¬ï¼Œ
             è§ sql/_probe/verify_inhospvisit_sql_expansion.py çš„ REACHABILITY_EVIDENCE)。
             ä½†ä»ç„¶ã€ç…§æŠ„保留】:保留后"与原 XML çš„ WHERE ç­‰ä»·"就是可机械验证的事实,
             è€Œä¸æ˜¯é æ–‡æ¡£è®ºè¯ã€‚代价只是几个永不进入的分支。
        -->
        <if test="inhospMydFlag != null ">and b.inhosp_myd_flag = #{inhospMydFlag}</if>
        <if test="managementDoctor != null ">and b.management_doctor = #{managementDoctor}</if>
        <if test="managementDoctorCode != null ">and b.management_doctor_code = #{managementDoctorCode}</if>
        <if test="inhospno != null ">and b.inhospno = #{inhospno}</if>
        <if test="bedNo != null ">and b.bed_no = #{bedNo}</if>
        <if test="outWayId != null ">and b.out_way_id = #{outWayId}</if>
        <if test="outWayName != null ">and b.out_way_name = #{outWayName}</if>
        <if test="encounterSourceId != null ">and b.encounter_source_id = #{encounterSourceId}</if>
        <if test="importanceFlagCode != null ">and b.importance_flag_code = #{importanceFlagCode}</if>
        <if test="emergencyLevelCode != null ">and b.emergency_level_code = #{emergencyLevelCode}</if>
        <if test="hospitalname != null  and hospitalname != ''">and hospitalname like concat('%', #{hospitalname},
            '%')
        </if>
        <if test="hospitaldistrictname != null  and hospitaldistrictname != ''">and hospitaldistrictname like
            concat('%', #{hospitaldistrictname}, '%')
        </if>
        <if test="cry != null and cry == 0 ">and b.endtime is null</if>
        <if test="cry != null and cry == 1 ">and b.inhospstate=1</if>
        <if test="cry != null and cry == 2 ">and b.inhospstate=2</if>
        <if test="cry != null and cry == 3 ">and b.inhospstate=3</if>
        <if test="inhospstate != null ">and b.inhospstate = #{inhospstate}</if>
        <if test="schemestatus != null ">and b.schemestatus = #{schemestatus}</if>
        <if test="leaveldeptcode != null ">and b.leaveldeptcode = #{leaveldeptcode}</if>
        <if test="leavediagname != null  and leavediagname != ''">and b.leavediagname like concat('%',
            #{leavediagname}, '%')
        </if>
        <if test="drname != null  and drname != ''">and b.drname like concat('%', #{drname}, '%')</if>
        <if test="leaveldeptname != null  and leaveldeptname != ''">and b.leaveldeptname like concat('%',
            #{leaveldeptname}, '%')
        </if>
        <if test="deptname != null  and deptname != ''">and b.deptname like concat('%', #{deptname}, '%')</if>
        <if test="patid != null ">and a.id = #{patid}</if>
        <if test="leavehospitaldistrictname != null  and leavehospitaldistrictname != ''">and
            b.leavehospitaldistrictname like concat('%', #{leavehospitaldistrictname}, '%')
        </if>
        <if test="medicalCode != null and medicalCode != ''">and b.medical_code = #{medicalCode}</if>
        <if test="medicalName != null and medicalName != ''">and b.medical_name = #{medicalName}</if>
        <if test="medicalCompany != null and medicalCompany != ''">and b.medical_company = #{medicalCompany}</if>
        <if test="leavehospitaldistrictcodes != null and leavehospitaldistrictcodes.size()>0">
            AND b.leavehospitaldistrictcode IN
            <foreach collection="leavehospitaldistrictcodes" item="leavehospitaldistrictcode" open="(" separator=","
                     close=")">
                #{leavehospitaldistrictcode}
            </foreach>
        </if>
        <if test="hospitaldistrictcodeList != null and hospitaldistrictcodeList.size()>0">
            AND b.hospitaldistrictcode IN
            <foreach collection="hospitaldistrictcodeList" item="hospitaldistrictcode" open="(" separator=","
                     close=")">
                #{hospitaldistrictcode}
            </foreach>
        </if>
        <if test="leaveldeptcodes != null and leaveldeptcodes.size()>0">
            AND b.leaveldeptcode IN
            <foreach collection="leaveldeptcodes" item="leaveldeptcode" open="(" separator="," close=")">
                #{leaveldeptcode}
            </foreach>
        </if>
        <if test="deptcodeList != null and deptcodeList.size()>0">
            AND b.deptcode IN
            <foreach collection="deptcodeList" item="deptcode" open="(" separator="," close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="icd10codeList != null and icd10codeList.size()>0">
            AND b.leaveicd10code IN
            <foreach collection="icd10codeList" item="icd10code" open="(" separator="," close=")">
                #{icd10code}
            </foreach>
        </if>
        GROUP BY
        b.inhospid,
        a.age,
        a.age_unit,
        a.telcode,
        a.id,
        a.sex
        <if test="cry != null and cry==1">
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==2">
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==3">
            order by b.endtime desc
        </if>
        <if test="cry != null and cry==0">
            <!--
              åœ¨é™¢æ‚£è€…:可按入院床位正/倒序(bedSort=asc æˆ– desc),不传时保持默认排序(入院时间倒序 + åºŠä½æ­£åºï¼‰ã€‚
              åºŠä½æŒ‰è‡ªç„¶åºæŽ’(只用 5.7 ä¹Ÿæ”¯æŒçš„ REGEXP/CAST/TRIM/LENGTH,不依赖 8.0 çš„ REGEXP_REPLACE):
                1) åºŠä½ä¸ºç©º/空串的排最后
                2) çº¯æ•°å­—床位排前面,按数值大小(2 åœ¨ 10 å‰ï¼‰
                3) å¸¦å­—母/汉字的床位按长度 + å­—符串(A2 åœ¨ A12 å‰ï¼‰
                4) inhospid å…œåº•,保证翻页顺序稳定
              å€’序只反转 2)、3) ä¸¤ç»„内部顺序:空床位仍排最后,纯数字仍在前。
              è¿™æ®µã€é€å­—照抄】原 XML,不改。
            -->
            <choose>
                <when test='"asc".equals(bedSort)'>
                    order by
                    coalesce(trim(b.bed_no), '') = '' ,
                    (trim(b.bed_no) regexp '^[0-9]+$') = 0 ,
                    cast(trim(b.bed_no) as unsigned) asc ,
                    length(trim(b.bed_no)) asc ,
                    trim(b.bed_no) asc ,
                    b.inhospid asc
                </when>
                <when test='"desc".equals(bedSort)'>
                    order by
                    coalesce(trim(b.bed_no), '') = '' ,
                    (trim(b.bed_no) regexp '^[0-9]+$') = 0 ,
                    cast(trim(b.bed_no) as unsigned) desc ,
                    length(trim(b.bed_no)) desc ,
                    trim(b.bed_no) desc ,
                    b.inhospid asc
                </when>
                <otherwise>
                    order by b.starttime desc , b.bed_no asc
                </otherwise>
            </choose>
        </if>
    </select>
    <!--
      æŒ‰ä¸»é”®æŸ¥ï¼ˆå¯¹åº”原 PatMedInhospMapper.xml çš„ selectPatMedInhospByInhospid,L678-681)。
      âš ï¸ åŽŸå®žçŽ°èµ° <sql id="selectPatMedInhospVo"/> å…±äº«ç‰‡æ®µï¼›è¿™é‡Œæ˜¾å¼åˆ—出同一批列,不复制片段。
      âš ï¸ å•表查询,不带 age / telcode / sex(原实现同样不带),所以按主键查出来的行这几个字段为 null。
         è¿™ä¸ŽåŽŸå®žçŽ°ä¸€è‡´ï¼šboxedServiceSubtask ç”¨åˆ°çš„æ‚£è€…字段一律来自 patArchive æŸ¥è¯¢ï¼Œä¸æ¥è‡ªè¿™é‡Œã€‚
    -->
    <select id="selectByInhospid" parameterType="Long" resultMap="VisitInhospRowResult">
        select inhospid,
               medical_code,
               patname,
               fuflag,
               management_doctor,
               management_doctor_code,
               fudate,
               remark,
               nurse_id,
               nurse_name,
               deptcheck_flag,
               wardcheck_flag,
               in_deptcheck_flag,
               in_wardcheck_flag,
               diagcheck_flag,
               schedulecheck_flag,
               inhospstate,
               patno,
               long_task_reason,
               inhospno,
               serialnum,
               hospitaldistrictcode,
               hospitaldistrictname,
               diagname,
               starttime,
               endtime,
               deptcode,
               deptname,
               orgid,
               patid,
               leavediagname,
               leaveicd10code,
               drcode,
               drname,
               leaveldeptcode,
               leaveldeptname,
               leavehospitaldistrictcode,
               leavehospitaldistrictname,
               campusid
        from pat_med_inhosp
        where inhospid = #{inhospid}
    </select>
    <!--
      åªå›žå†™éšè®¿æ ‡å¿—位与 long_task_reason。
      âš ï¸ åŽŸå®žçŽ°æ˜¯ updatePatMedInhosp çš„"近百个 <if>:传什么更新什么"。随访侧只写这几列,
         æ‰€ä»¥è¿™é‡Œæ˜¾å¼åˆ—出 â€”— é¿å…"传了个半满实体导致某列被意外写空/漏写"这类静默问题。
      âš ï¸ æ ‡å¿—位列名走【白名单 <choose> å±•开成常量列名】,不做 ${} å­—符串拼接:既防注入,
         åˆè®©"哪些列可写"在代码里可枚举。
      âš ï¸ reason ä¸º null æ—¶ã€ä¸æ”¹åŠ¨ã€‘long_task_reason(与原 <if test="longTaskReason != null"> ä¸€è‡´ï¼‰ã€‚
    -->
    <update id="updateFlags">
        update pat_med_inhosp
        <trim prefix="SET" suffixOverrides=",">
            <choose>
                <when test='flagColumn == "deptcheck_flag"'>deptcheck_flag = #{flagValue},</when>
                <when test='flagColumn == "wardcheck_flag"'>wardcheck_flag = #{flagValue},</when>
                <when test='flagColumn == "diagcheck_flag"'>diagcheck_flag = #{flagValue},</when>
                <when test='flagColumn == "medcheck_flag"'>medcheck_flag = #{flagValue},</when>
                <when test='flagColumn == "in_deptcheck_flag"'>in_deptcheck_flag = #{flagValue},</when>
                <when test='flagColumn == "in_wardcheck_flag"'>in_wardcheck_flag = #{flagValue},</when>
                <when test='flagColumn == "schedulecheck_flag"'>schedulecheck_flag = #{flagValue},</when>
                <otherwise>
                    <!-- éžæ³•列名:自赋值,保证 SET ä¸ä¸ºç©ºä¸”不改数据(Service å±‚会先拦掉) -->
                    inhospid = inhospid,
                </otherwise>
            </choose>
            <if test="reason != null">long_task_reason = #{reason},</if>
        </trim>
        where inhospid = #{inhospid}
    </update>
    <!--
      åªå›žå†™ remark(原 updateCheckFlag çš„附带动作,PatMedInhospServiceImpl L1680-1683)。
      ä¸ŽåŽŸå®žçŽ°"一次整实体更新"在结果上等价:原 updatePatMedInhosp æ¯ä¸ªå­—段各自 <if>,
      updateCheckFlag é‚£æ¬¡è°ƒç”¨åªæœ‰ flag ä¸Ž remark ä¸¤ä¸ªå­—段非 null。
    -->
    <update id="updateRemark">
        update pat_med_inhosp
        set remark = #{remark}
        where inhospid = #{inhospid}
    </update>
</mapper>
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitPreachformMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,331 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inhospvisit.mapper.VisitPreachformMapper">
    <!--
      ============================================================================
      å‡ºå…¥é™¢éšè®¿æ¨¡å—自带的 service_subtask_preachform è¯»å†™ã€‚
      ============================================================================
      ä¸Ž smartor çš„ mapper/smartor/ServiceSubtaskPreachformMapper.xml ç­‰ä»·ï¼š
        Â· selectList â† selectServiceSubtaskPreachformList(原 XML L44-73)
        Â· insert     â† insertServiceSubtaskPreachform(原 XML L81-151,useGeneratedKeys)
      åŽŸ XML ç”¨ <sql id="selectServiceSubtaskPreachformVo"> å…±äº«ç‰‡æ®µï¼›è¿™é‡Œã€æ˜¾å¼åˆ—出同一批列】,
      ä¸å¤åˆ¶ç‰‡æ®µï¼ˆä¸Ž VisitInhospMapper åŒä¸€åšæ³•:自包含、便于逐条比对)。
      ============================================================================
    -->
    <resultMap type="com.ruoyi.common.core.service.dto.VisitPreachformRow" id="VisitPreachformRowResult">
        <result property="id" column="id"/>
        <result property="subid" column="subid"/>
        <result property="taskid" column="taskid"/>
        <result property="preachform" column="preachform"/>
        <result property="sendstate" column="sendstate"/>
        <result property="currentSendTime" column="current_send_time"/>
        <result property="remark" column="remark"/>
        <result property="orgid" column="orgid"/>
        <result property="delFlag" column="del_flag"/>
        <result property="createTime" column="create_time"/>
        <result property="createBy" column="create_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="updateBy" column="update_by"/>
        <result property="compensateTime" column="compensate_time"/>
        <result property="sort" column="sort"/>
    </resultMap>
    <!-- æ¡ä»¶ä¸Žé¡ºåºé€æ¡å¯¹é½åŽŸ XML L44-73 -->
    <select id="selectList" parameterType="com.ruoyi.common.core.service.dto.VisitPreachformRow"
            resultMap="VisitPreachformRowResult">
        select id,
               subid,
               taskid,
               preachform,
               sendstate,
               current_send_time,
               remark,
               orgid,
               del_flag,
               create_time,
               create_by,
               update_time,
               update_by,
               compensate_time,
               sort
        from service_subtask_preachform
        where 1=1
        and del_flag = 0
        <if test="subid != null ">
            and subid = #{subid}
        </if>
        <if test="taskid != null ">
            and taskid = #{taskid}
        </if>
        <if test="currentSendTime != null ">
            and current_send_time = #{currentSendTime}
        </if>
        <if test="preachform != null  and preachform != ''">
            and preachform = #{preachform}
        </if>
        <if test="sendstate != null  and sendstate != ''">
            and sendstate = #{sendstate}
        </if>
        <if test="orgid != null  and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="sort != null ">
            and sort = #{sort}
        </if>
        <if test="compensateTime != null ">
            and compensate_time = #{compensateTime}
        </if>
    </select>
    <!-- åˆ—与顺序逐条对齐原 XML L81-151 -->
    <insert id="insert" parameterType="com.ruoyi.common.core.service.dto.VisitPreachformRow"
            useGeneratedKeys="true" keyProperty="id">
        insert into service_subtask_preachform
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">id,</if>
            <if test="subid != null">subid,</if>
            <if test="taskid != null">taskid,</if>
            <if test="preachform != null">preachform,</if>
            <if test="sendstate != null">sendstate,</if>
            <if test="remark != null">remark,</if>
            <if test="orgid != null">orgid,</if>
            <if test="delFlag != null">del_flag,</if>
            <if test="createTime != null">create_time,</if>
            <if test="createBy != null">create_by,</if>
            <if test="updateTime != null">update_time,</if>
            <if test="updateBy != null">update_by,</if>
            <if test="sort != null">sort,</if>
            <if test="compensateTime != null">compensate_time,</if>
            <if test="currentSendTime != null ">current_send_time,</if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">#{id},</if>
            <if test="subid != null">#{subid},</if>
            <if test="taskid != null">#{taskid},</if>
            <if test="preachform != null">#{preachform},</if>
            <if test="sendstate != null">#{sendstate},</if>
            <if test="remark != null">#{remark},</if>
            <if test="orgid != null">#{orgid},</if>
            <if test="delFlag != null">#{delFlag},</if>
            <if test="createTime != null">#{createTime},</if>
            <if test="createBy != null">#{createBy},</if>
            <if test="updateTime != null">#{updateTime},</if>
            <if test="updateBy != null">#{updateBy},</if>
            <if test="sort != null">#{sort},</if>
            <if test="compensateTime != null">#{compensateTime},</if>
            <if test="currentSendTime != null ">#{currentSendTime},</if>
        </trim>
    </insert>
    <!--
      ============================================================================
      è·¨æ¨¡å— SQL æ”¶å£è¡¥é½çš„ 7 ä¸ª statement(照抄 smartor åŽŸ XML,条件/列/顺序不变)
        Â· selectById             â† selectServiceSubtaskPreachformById
        Â· selectByTaskidAndOrgid â† selectBySubtask(原按 taskid + orgid,非主键)
        Â· updateById             â† updateServiceSubtaskPreachform(where id = #{id})
        Â· updateByCondition      â† updateSSPByCondition(SET åŒ updateById,WHERE æŒ‰æ¡ä»¶ä¸”æ—  id)
        Â· softDeleteById         â† deleteServiceSubtaskPreachformById(update ç½® del_flag=1)
        Â· softDeleteByIds        â† deleteServiceSubtaskPreachformByIds(同上,in é›†åˆï¼‰
        Â· softDeleteBySubid      â† deleteServiceSubtaskPreachformBySubid(同上,按 subid)
      æ³¨æ„ï¼šä¸‰ä¸ª delete* åœ¨åŽŸ XML é‡Œå°±æ˜¯"软删"(update set del_flag=1),此处原样保留;
            å…¶ä¸­ BySubid åŽŸæœ¬ç”¨çš„å°±æ˜¯ <delete> æ ‡ç­¾ï¼Œä¹Ÿç…§æŠ„。
      ============================================================================
    -->
    <select id="selectById" parameterType="Long" resultMap="VisitPreachformRowResult">
        select id,
               subid,
               taskid,
               preachform,
               sendstate,
               current_send_time,
               remark,
               orgid,
               del_flag,
               create_time,
               create_by,
               update_time,
               update_by,
               compensate_time,
               sort
        from service_subtask_preachform
        where id = #{id}
    </select>
    <!-- åŽŸ selectBySubtask:del_flag=0 + å¯é€‰ taskid/orgid(原参数是 ServiceSubtask,实际只用这两个字段) -->
    <select id="selectByTaskidAndOrgid" resultMap="VisitPreachformRowResult">
        select id,
               subid,
               taskid,
               preachform,
               sendstate,
               current_send_time,
               remark,
               orgid,
               del_flag,
               create_time,
               create_by,
               update_time,
               update_by,
               compensate_time,
               sort
        from service_subtask_preachform
        WHERE del_flag = 0
        <if test="taskid != null">
            AND taskid = #{taskid}
        </if>
        <if test="orgid != null and orgid != ''">
            AND orgid = #{orgid}
        </if>
    </select>
    <!-- åŽŸ updateServiceSubtaskPreachform:where id = #{id} -->
    <update id="updateById" parameterType="com.ruoyi.common.core.service.dto.VisitPreachformRow">
        update service_subtask_preachform
        <trim prefix="SET" suffixOverrides=",">
            <if test="subid != null">subid =
                #{subid},
            </if>
            <if test="taskid != null">taskid =
                #{taskid},
            </if>
            <if test="preachform != null">preachform =
                #{preachform},
            </if>
            <if test="sendstate != null">sendstate =
                #{sendstate},
            </if>
            <if test="remark != null">remark =
                #{remark},
            </if>
            <if test="orgid != null">orgid =
                #{orgid},
            </if>
            <if test="delFlag != null">del_flag =
                #{delFlag},
            </if>
            <if test="createTime != null">create_time =
                #{createTime},
            </if>
            <if test="createBy != null">create_by =
                #{createBy},
            </if>
            <if test="updateTime != null">update_time =
                #{updateTime},
            </if>
            <if test="updateBy != null">update_by =
                #{updateBy},
            </if>
            <if test="sort != null">sort =
                #{sort},
            </if>
            <if test="compensateTime != null">compensate_time =
                #{compensateTime},
            </if>
            <if test="currentSendTime != null ">
                current_send_time = #{currentSendTime},
            </if>
        </trim>
        where id = #{id}
    </update>
    <!-- åŽŸ updateSSPByCondition:SET ä¸Ž updateById ç›¸åŒï¼ŒWHERE æŒ‰æ¡ä»¶ä¸”æ—  id -->
    <update id="updateByCondition" parameterType="com.ruoyi.common.core.service.dto.VisitPreachformRow">
        update service_subtask_preachform
        <trim prefix="SET" suffixOverrides=",">
            <if test="subid != null">subid =
                #{subid},
            </if>
            <if test="taskid != null">taskid =
                #{taskid},
            </if>
            <if test="preachform != null">preachform =
                #{preachform},
            </if>
            <if test="sendstate != null">sendstate =
                #{sendstate},
            </if>
            <if test="remark != null">remark =
                #{remark},
            </if>
            <if test="orgid != null">orgid =
                #{orgid},
            </if>
            <if test="delFlag != null">del_flag =
                #{delFlag},
            </if>
            <if test="createTime != null">create_time =
                #{createTime},
            </if>
            <if test="createBy != null">create_by =
                #{createBy},
            </if>
            <if test="updateTime != null">update_time =
                #{updateTime},
            </if>
            <if test="updateBy != null">update_by =
                #{updateBy},
            </if>
            <if test="sort != null">sort =
                #{sort},
            </if>
            <if test="compensateTime != null">compensate_time =
                #{compensateTime},
            </if>
            <if test="currentSendTime != null ">
                current_send_time = #{currentSendTime},
            </if>
        </trim>
        where 1=1
        <if test="subid != null">and subid =
            #{subid}
        </if>
        <if test="taskid != null">and taskid =
            #{taskid}
        </if>
        <if test="preachform != null">and preachform =
            #{preachform}
        </if>
        <if test="remark != null">and remark =
            #{remark}
        </if>
        <if test="orgid != null">and orgid =
            #{orgid}
        </if>
    </update>
    <!-- åŽŸ deleteServiceSubtaskPreachformById:<update> ç½® del_flag=1 -->
    <update id="softDeleteById" parameterType="Long">
        update service_subtask_preachform
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id = #{id}
    </update>
    <!-- åŽŸ deleteServiceSubtaskPreachformByIds:<update> + in é›†åˆ -->
    <update id="softDeleteByIds" parameterType="String">
        update service_subtask_preachform
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </update>
    <!-- åŽŸ deleteServiceSubtaskPreachformBySubid:<delete> æ ‡ç­¾ä½†è¯­å¥æ˜¯ update -->
    <delete id="softDeleteBySubid" parameterType="Long">
        update service_subtask_preachform
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where subid = #{subid}
    </delete>
</mapper>
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitTaskdeptMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,84 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inhospvisit.mapper.VisitTaskdeptMapper">
    <!--
      ============================================================================
      å‡ºå…¥é™¢éšè®¿æ¨¡å—自带的 service_taskdept åªè¯»æŸ¥è¯¢ã€‚
      ============================================================================
      å¯¹åº” smartor çš„ ServiceTaskdeptMapper.xml#selectServiceTaskdeptList(L40-72)。
      åŽŸ XML ç”¨ <sql id="selectServiceTaskdeptVo"> å…±äº«ç‰‡æ®µï¼›è¿™é‡Œæ˜¾å¼åˆ—出同一批列,不复制片段。
      âš ï¸ åŽŸ XML çš„ bug(dept_name æ¡ä»¶åˆ¤çš„æ˜¯ deptCode)已【原样照抄】——
         è§ VisitTaskdeptMapper çš„类注释。随访调用点从不设 deptName,所以不可达,
         ä½†æŒ‰ç¬¬ 6d æ­¥çš„原则:不靠"不可达"来删条件。
      ============================================================================
    -->
    <resultMap type="com.ruoyi.inhospvisit.domain.row.VisitTaskdeptRow" id="VisitTaskdeptRowResult">
        <result property="id" column="id"/>
        <result property="taskId" column="task_id"/>
        <result property="taskName" column="task_name"/>
        <result property="longtask" column="longtask"/>
        <result property="serviceType" column="service_type"/>
        <result property="deptType" column="dept_type"/>
        <result property="deptId" column="dept_id"/>
        <result property="deptCode" column="dept_code"/>
        <result property="deptName" column="dept_name"/>
        <result property="delFlag" column="del_flag"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
        <result property="orgid" column="orgid"/>
    </resultMap>
    <select id="selectList" parameterType="com.ruoyi.inhospvisit.domain.row.VisitTaskdeptRow"
            resultMap="VisitTaskdeptRowResult">
        select id,
               task_id,
               dept_name,
               task_name,
               orgid,
               longtask,
               service_type,
               dept_type,
               dept_id,
               dept_code,
               del_flag,
               create_time,
               update_time
        from service_taskdept
        where 1=1
            and del_flag = 0
            <if test="taskId != null ">
                and task_id = #{taskId}
            </if>
            <if test="taskName != null ">
                and task_name = #{taskName}
            </if>
            <if test="longtask != null ">
                and longtask = #{longtask}
            </if>
            <if test="serviceType != null  and serviceType != ''">
                and service_type = #{serviceType}
            </if>
            <if test="deptType != null  and deptType != ''">
                and dept_type = #{deptType}
            </if>
            <if test="deptId != null ">
                and dept_id = #{deptId}
            </if>
            <if test="deptCode != null  and deptCode != ''">
                and dept_code = #{deptCode}
            </if>
            <!-- âš ï¸ åŽŸ XML è¿™é‡Œåˆ¤çš„æ˜¯ deptCode(不是 deptName)—— åŽŸæ ·ç…§æŠ„ -->
            <if test="deptName != null  and deptCode != ''">
                and dept_name = #{deptName}
            </if>
            <if test="orgid != null  and orgid != ''">
                and orgid = #{orgid}
            </if>
    </select>
</mapper>
ruoyi-inhospvisit/src/main/resources/mapper/inhospvisit/VisitTaskdiagMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.inhospvisit.mapper.VisitTaskdiagMapper">
    <!--
      ============================================================================
      å‡ºå…¥é™¢éšè®¿æ¨¡å—自带的 service_taskdiag åªè¯»æŸ¥è¯¢ã€‚
      ============================================================================
      å¯¹åº” smartor çš„ ServiceTaskdiagMapper.xml#selectServiceTaskdiagListByIcdName(L104-154)。
      åŽŸ XML ç”¨ <sql id="selectServiceTaskdiagVo"> å…±äº«ç‰‡æ®µï¼›è¿™é‡Œæ˜¾å¼åˆ—出同一批列。
      æ¡ä»¶é¡ºåºä¸Žå†…容逐条照抄(含 guid/pguid/pid/dept_name/ward_name è¿™äº›éšè®¿ä¾§ä¸ç”¨çš„æ¡ä»¶ï¼‰ã€‚
      ============================================================================
    -->
    <resultMap type="com.ruoyi.inhospvisit.domain.row.VisitTaskdiagRow" id="VisitTaskdiagRowResult">
        <result property="id" column="id"/>
        <result property="taskId" column="task_id"/>
        <result property="longtask" column="longtask"/>
        <result property="taskName" column="task_name"/>
        <result property="serviceType" column="service_type"/>
        <result property="icd10id" column="icd10id"/>
        <result property="icd10code" column="icd10code"/>
        <result property="icd10name" column="icd10name"/>
        <result property="delFlag" column="del_flag"/>
        <result property="orgid" column="orgid"/>
        <result property="guid" column="guid"/>
        <result property="pguid" column="pguid"/>
        <result property="pid" column="pid"/>
        <result property="deptCode" column="dept_code"/>
        <result property="deptName" column="dept_name"/>
        <result property="wardCode" column="ward_code"/>
        <result property="wardName" column="ward_name"/>
        <result property="createTime" column="create_time"/>
        <result property="updateTime" column="update_time"/>
    </resultMap>
    <select id="selectListByIcdName" parameterType="com.ruoyi.inhospvisit.domain.row.VisitTaskdiagRow"
            resultMap="VisitTaskdiagRowResult">
        select id,
               task_id,
               longtask,
               task_name,
               service_type,
               icd10id,
               icd10code,
               icd10name,
               del_flag,
               create_time,
               update_time,
               orgid,
               guid,
               pguid,
               pid,
               dept_code,
               dept_name,
               ward_code,
               ward_name
        from service_taskdiag
        where 1=1
        and del_flag = 0
        <if test="taskId != null ">
            and task_id = #{taskId}
        </if>
        <if test="longtask != null ">
            and longtask = #{longtask}
        </if>
        <if test="taskName != null  and taskName != ''">
            and task_name like concat('%', #{taskName}, '%')
        </if>
        <if test="serviceType != null  and serviceType != ''">
            and service_type = #{serviceType}
        </if>
        <if test="icd10id != null ">
            and icd10id = #{icd10id}
        </if>
        <if test="icd10code != null  and icd10code != ''">
            and icd10code = #{icd10code}
        </if>
        <if test="icd10name != null  and icd10name != ''">
            and icd10name = #{icd10name}
        </if>
        <if test="orgid != null  and orgid != ''">
            and orgid = #{orgid}
        </if>
        <if test="guid != null  and guid != ''">
            and guid = #{guid}
        </if>
        <if test="pguid != null  and pguid != ''">
            and pguid = #{pguid}
        </if>
        <if test="pid != null ">
            and pid = #{pid}
        </if>
        <if test="deptCode != null  and deptCode != ''">
            and dept_code = #{deptCode}
        </if>
        <if test="deptName != null  and deptName != ''">
            and dept_name = #{deptName}
        </if>
        <if test="wardCode != null  and wardCode != ''">
            and ward_code = #{wardCode}
        </if>
        <if test="wardName != null  and wardName != ''">
            and ward_name = #{wardName}
        </if>
    </select>
</mapper>
ruoyi-inhospvisit/src/test/java/com/ruoyi/inhospvisit/api/InhospVisitContextCheck.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,446 @@
//package com.ruoyi.inhospvisit.api;
//
//import com.ruoyi.common.core.service.IConfigService;
//import com.ruoyi.common.core.service.dto.VisitAgainSubtaskDto;
//import com.ruoyi.inhospvisit.config.InhospVisitAvailabilityInterceptor;
//import com.ruoyi.inhospvisit.config.InhospVisitProperties;
//import com.ruoyi.inhospvisit.config.InhospVisitSwitch;
//import com.ruoyi.inhospvisit.config.InhospVisitWebConfig;
//import org.springframework.beans.factory.ObjectProvider;
//import org.springframework.context.annotation.AnnotationConfigApplicationContext;
//
//import java.util.concurrent.atomic.AtomicInteger;
//
///**
// * å‡ºå…¥é™¢éšè®¿æ¨¡å—çš„ <b>Spring å®¹å™¨è£…配校验</b>(不依赖 JUnit、不依赖数据库)。
// *
// * <p>照抄 {@code SatisfactionContextCheck} çš„做法:用真实的(但极简的)
// * {@link AnnotationConfigApplicationContext} éªŒè¯"Spring èƒ½æŠŠ Bean è£…起来" â€”—
// * é™æ€æ£€æŸ¥è¯æ˜Žä¸äº†è£…配。满意度模块当时有<b>两个真实缺陷</b>只有真起容器才能发现:
// * <ol>
// *   <li>门面有两个公开构造器却没标 {@code @Autowired} â†’ å¯åŠ¨æŠ¥ {@code No default constructor found};</li>
// *   <li>门面把自己当实现 â†’ è°ƒç”¨æ—¶æ— é™é€’å½’ â†’ {@code StackOverflowError}。</li>
// * </ol>
// * æœ¬ç±»æŠŠè¿™ä¸¤æ¡éƒ½åšæˆæ–­è¨€ã€‚
// *
// * <h3>运行方式(仓库内没有现成脚本)</h3>
// * <pre>
// * mvn -o -q -pl ruoyi-inhospvisit -am test-compile -DskipTests
// * # ç„¶åŽç”¨æ¨¡å— classpath ç›´æŽ¥è·‘ main(surefire è®¤ä¸å‡ºå®ƒï¼Œå› ä¸ºæ²¡æœ‰ @Test)
// * </pre>
// * classpath å¯ç”¨ {@code mvn dependency:build-classpath -Dmdep.outputFile=...} å¯¼å‡ºã€‚
// */
//public class InhospVisitContextCheck {
//
//    private static int failed = 0;
//
//    private static void check(String name, String expected, String actual) {
//        if (expected.equals(actual)) {
//            System.out.println("  [OK]   " + name + " = " + actual);
//        } else {
//            System.out.println("  [FAIL] " + name + " æœŸæœ›=[" + expected + "] å®žé™…=[" + actual + "]");
//            failed++;
//        }
//    }
//
//    public static void main(String[] args) {
//        System.out.println("=== åœºæ™¯ 1:参数表 inhospvisit.enabled=true,且存在内部实现 ===");
//        try (AnnotationConfigApplicationContext ctx = context(true, true, SYS_ON)) {
//            InhospVisitFacade facade = ctx.getBean(InhospVisitFacade.class);
//            check("门面能被 Spring å®žä¾‹åŒ–(构造器不再歧义)", "true", String.valueOf(facade != null));
//            check("启用时 isEnabled()", "true", String.valueOf(facade.isEnabled()));
//            check("取到的是【内部实现】而不是门面自己", "1",
//                    String.valueOf(facade.dealOutHospInfo("7")));
//            check("门面同时是 PublicitySubtaskApi(下沉给宣教模块的那一个)", "true",
//                    String.valueOf(ctx.getBean(com.ruoyi.common.core.service.PublicitySubtaskApi.class) == facade));
//            // å®¹å™¨é‡Œåº”有 2 ä¸ª InhospVisitApi:门面自己 + å†…部实现(门面也实现该接口,这是设计使然)
//            check("容器内 InhospVisitApi å®žçŽ°æ•° = é—¨é¢ 1 + å†…部实现 1", "2",
//                    String.valueOf(ctx.getBeansOfType(InhospVisitApi.class).size()));
//            check("开关读取的键名是 sys_config çš„ inhospvisit.enabled", "inhospvisit.enabled",
//                    InhospVisitSwitch.CONFIG_KEY);
//        }
//
//        System.out.println();
//        System.out.println("=== åœºæ™¯ 2:参数表=true,但【没有】内部实现(模块没被打进应用)===");
//        try (AnnotationConfigApplicationContext ctx = context(true, false, SYS_ON)) {
//            InhospVisitFacade facade = ctx.getBean(InhospVisitFacade.class);
//            check("无实现时 isEnabled() ä¸º false", "false", String.valueOf(facade.isEnabled()));
//            // å…³é”®ï¼šä¸èƒ½æŠŠé—¨é¢è‡ªå·±å½“实现,否则这里会 StackOverflowError
//            String result;
//            try {
//                result = String.valueOf(facade.dealOutHospInfo("7"));
//            } catch (StackOverflowError e) {
//                result = "StackOverflowError";
//            }
//            check("无实现时 dealOutHospInfo å®‰å…¨è¿”回 0(不会自引用递归)", "0", result);
//
//            String again;
//            try {
//                VisitAgainSubtaskDto dto = facade.buildAgainVisitDraft("ORG1", 1L, 2L, 2L);
//                again = String.valueOf(dto.isInhospFound());
//            } catch (StackOverflowError e) {
//                again = "StackOverflowError";
//            }
//            check("无实现时 buildAgainVisitDraft è¿”回「未找到出入院记录」而不是递归", "false", again);
//
//            check("无实现时 generatePublicitySubtask å®‰å…¨è¿”回 false", "false",
//                    String.valueOf(facade.generatePublicitySubtask(1L, 1L, 1L, 4, "7")));
//
//            // åè¯ï¼šç›´æŽ¥é—®å®¹å™¨"按类型有哪些候选",看它是否把门面自己算进去
//            ProviderProbe probe = ctx.getBean(ProviderProbe.class);
//            check("无实现时,容器按类型只找到门面自己(证明自引用兜底存在)", "true",
//                    String.valueOf(probe.rawCandidate() == facade));
//            check("但门面不会把'自己'当成实现(self è¿‡æ»¤ç”Ÿæ•ˆï¼‰", "false",
//                    String.valueOf(facade.isEnabled()));
//        }
//
//        System.out.println();
//        System.out.println("=== åœºæ™¯ 3:参数表 inhospvisit.enabled=false(实现 Bean ä¾ç„¶å­˜åœ¨ï¼Œä½†é—¨é¢å¿…须不转发)===");
//        StubApi.CALLS.set(0);
//        try (AnnotationConfigApplicationContext ctx = context(true, true, SYS_OFF)) {
//            InhospVisitFacade facade = ctx.getBean(InhospVisitFacade.class);
//            check("关闭时 isEnabled() ä¸º false", "false", String.valueOf(facade.isEnabled()));
//            String result;
//            try {
//                result = String.valueOf(facade.dealOutHospInfo("7"));
//            } catch (StackOverflowError e) {
//                result = "StackOverflowError";
//            }
//            check("关闭时 dealOutHospInfo è¿”回 0(不会自递归)", "0", result);
//            check("关闭时不再调用内部实现(stub è°ƒç”¨æ¬¡æ•° = 0)", "0",
//                    String.valueOf(StubApi.CALLS.get()));
//
//            // ------------------------------------------------------------------
//            // å…³é”®ï¼šåŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯çš„读写【不受本开关管辖】
//            //   ç†ç”±è§ InhospVisitDataService ç±»æ³¨é‡Š â€”— HIS é‡‡é›†ä¸Žæ¡£æ¡ˆæ±‡èšéƒ½ä¾èµ–它写入院记录,
//            //   è‹¥æŠŠå¼€å…³ä¸€å¹¶å¥—上,关闭随访生成会连带让数据采集瘫痪,那是两件事。
//            // ------------------------------------------------------------------
//            StubDataService.CALLS.set(0);
//            String dataResult;
//            try {
//                dataResult = String.valueOf(facade.selectPatMedInhospByInhospid(123L) != null);
//            } catch (StackOverflowError e) {
//                dataResult = "StackOverflowError";
//            }
//            check("关闭时【数据查询】仍可用(不受开关管辖)", "true", dataResult);
//            check("关闭时【数据查询】确实转发到数据服务(调用次数 = 1)", "1",
//                    String.valueOf(StubDataService.CALLS.get()));
//
//            StubDataService.CALLS.set(0);
//            String updResult;
//            try {
//                updResult = String.valueOf(facade.updatePatMedInhosp(
//                        new com.ruoyi.inhospvisit.domain.PatMedInhosp()));
//            } catch (StackOverflowError e) {
//                updResult = "StackOverflowError";
//            }
//            check("关闭时【数据写入】仍可用且返回真实行数(不受开关管辖)", "7", updResult);
//            check("关闭时【数据写入】确实转发到数据服务(调用次数 = 1)", "1",
//                    String.valueOf(StubDataService.CALLS.get()));
//
//            InhospVisitAvailabilityInterceptor interceptor = ctx.getBean(InhospVisitAvailabilityInterceptor.class);
//            String thrown = "(未抛异常)";
//            try {
//                interceptor.preHandle(null, null, null);
//            } catch (Exception e) {
//                thrown = e.getClass().getSimpleName() + "|" + e.getMessage();
//            }
//            check("关闭时拦截器抛 ServiceException", "true", String.valueOf(thrown.startsWith("ServiceException|")));
//            check("关闭时错误提示明确(含键名与「接口不可用」)", "true",
//                    String.valueOf(thrown.contains("inhospvisit.enabled=false") && thrown.contains("接口不可用")));
//            check("关闭时错误提示说明定时任务不受开关控制", "true",
//                    String.valueOf(thrown.contains("定时任务不受此开关控制")));
//
//            check("关闭时受保护路径 2 æ¡ï¼ˆé˜¶æ®µä¸€ï¼šä¸å« addOperationSubTask)", "2",
//                    String.valueOf(InhospVisitWebConfig.guardedPathPatterns().size()));
//            check("受保护路径不含出入院记录 CRUD ç«¯ç‚¹", "false",
//                    String.valueOf(InhospVisitWebConfig.guardedPathPatterns().stream()
//                            .anyMatch(p -> p.contains("selectPatMedInhospList") || p.endsWith("/add")
//                                    || p.endsWith("/edit") || p.endsWith("/export"))));
//        }
//
//        System.out.println();
//        System.out.println("=== åœºæ™¯ 4:参数表【没有】inhospvisit.enabled è¿™ä¸€è¡Œæ—¶ï¼Œå›žè½ application.yml ===");
//        try (AnnotationConfigApplicationContext ctx = context(true, true, SYS_MISSING)) {
//            InhospVisitFacade facade = ctx.getBean(InhospVisitFacade.class);
//            check("参数行缺失、yml é»˜è®¤ true æ—¶ isEnabled() ä¸º true", "true", String.valueOf(facade.isEnabled()));
//        }
//        try (AnnotationConfigApplicationContext ctx = context(false, true, SYS_MISSING)) {
//            InhospVisitFacade facade = ctx.getBean(InhospVisitFacade.class);
//            check("参数行缺失、yml=false æ—¶ isEnabled() ä¸º false", "false", String.valueOf(facade.isEnabled()));
//        }
//
//        System.out.println();
//        System.out.println("=== åœºæ™¯ 5:容器里【没有】IConfigService æ—¶ï¼ˆç‹¬ç«‹/测试环境)必须能启动且按 yml å…œåº• ===");
//        try (AnnotationConfigApplicationContext ctx = context(true, true, null, false)) {
//            InhospVisitFacade facade = ctx.getBean(InhospVisitFacade.class);
//            check("无配置服务时门面仍能装配并启动", "true", String.valueOf(facade != null));
//            check("无配置服务时按 yml å…œåº•(true)", "true", String.valueOf(facade.isEnabled()));
//        }
//
//        System.out.println();
//        if (failed == 0) {
//            System.out.println("===== å‡ºå…¥é™¢éšè®¿æ¨¡å— Spring è£…配校验全部通过 =====");
//        } else {
//            System.out.println("===== å‡ºå…¥é™¢éšè®¿æ¨¡å— Spring è£…配校验失败 " + failed + " é¡¹ =====");
//            System.exit(1);
//        }
//    }
//
//    /**
//     * å‚数表的模拟取值
//     */
//    static final class Cfg {
//        /** å‚数行存在且为 true */
//        static final String SYS_ON = "true";
//        /** å‚数行存在且为 false */
//        static final String SYS_OFF = "false";
//        /** å‚数行不存在 */
//        static final String SYS_MISSING = null;
//    }
//
//    private static final String SYS_ON = Cfg.SYS_ON;
//    private static final String SYS_OFF = Cfg.SYS_OFF;
//    private static final String SYS_MISSING = Cfg.SYS_MISSING;
//
//    private static AnnotationConfigApplicationContext context(boolean ymlEnabled,
//                                                             boolean withImpl,
//                                                             String sysConfigValue) {
//        return context(ymlEnabled, withImpl, sysConfigValue, true);
//    }
//
//    private static AnnotationConfigApplicationContext context(boolean ymlEnabled,
//                                                             boolean withImpl,
//                                                             String sysConfigValue,
//                                                             boolean withConfigService) {
//        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//        InhospVisitProperties properties = new InhospVisitProperties();
//        properties.setEnabled(ymlEnabled);
//        ctx.registerBean(InhospVisitProperties.class, () -> properties);
//        ctx.register(InhospVisitFacade.class,
//                InhospVisitSwitch.class,
//                InhospVisitAvailabilityInterceptor.class,
//                InhospVisitWebConfig.class,
//                ProviderProbe.class,
//                StubDataService.class);
//        if (withConfigService) {
//            // å‡çš„参数配置服务:只认 inhospvisit.enabled è¿™ä¸€ä¸ªé”®ï¼Œå€¼ç”± sysConfigValue å†³å®š
//            ctx.registerBean("stubConfigService", IConfigService.class,
//                    () -> (IConfigService) configKey ->
//                            InhospVisitSwitch.CONFIG_KEY.equals(configKey) ? sysConfigValue : "");
//        }
//        if (withImpl) {
//            ctx.registerBean("stubInhospVisitApi", InhospVisitApi.class, StubApi::new);
//        }
//        ctx.refresh();
//        return ctx;
//    }
//
//    /**
//     * é¡¶æ›¿ {@code DefaultInhospVisitApi} çš„æ¡©ï¼šåªå›žå¡«è°ƒç”¨æ¬¡æ•°ä¸Žå›ºå®šè¿”回值,不碰数据库。
//     * è¿™æ ·æœ¬æ ¡éªŒå¯ä»¥åœ¨æ²¡æœ‰æ•°æ®æºçš„环境里跑。
//     */
//    public static class StubApi implements InhospVisitApi {
//
//        /**
//         * è°ƒç”¨æ¬¡æ•°ï¼šç”¨æ¥è¯æ˜Ž"开关关闭时门面确实没有转发给实现"
//         */
//        static final AtomicInteger CALLS = new AtomicInteger();
//
//        @Override
//        public boolean isEnabled() {
//            return true;
//        }
//
//        @Override
//        public int dealOutHospInfo(String config) {
//            CALLS.incrementAndGet();
//            return 1;
//        }
//
//        @Override
//        public int dealOutHospInfoBySchedule(String config) {
//            CALLS.incrementAndGet();
//            return 1;
//        }
//
//        /**
//         * è¡¥é½ {@link InhospVisitApi} çš„æŠ½è±¡æ–¹æ³•。
//         * <p>
//         * âš ï¸ è¿™ä¸¤ä¸ªæ–¹æ³•此前在本桩里<b>缺失</b>,导致本测试类从未编译成功
//         * ï¼ˆ{@code target/test-classes} ä¸€ç›´æ˜¯ç©ºçš„)——即"装配校验"实际上一次都没跑起来过。
//         * è¿™æ˜¯è¢«å‘现的既有缺陷,此处补上。
//         */
//        @Override
//        public int addOperationSubTask(String config) {
//            CALLS.incrementAndGet();
//            return 1;
//        }
//
//        /**
//         * è¡¥é½ {@link InhospVisitApi} çš„æŠ½è±¡æ–¹æ³•(见 {@link #addOperationSubTask})。
//         * <p>
//         * é—¨è¯Šéšè®¿å±žç¬¬äºŒæ‰¹ {@code ruoyi-outpatientvisit},桩里只记调用并返回 0。
//         */
//        @Override
//        public int dealOutpatientInfo() {
//            CALLS.incrementAndGet();
//            return 0;
//        }
//
//        @Override
//        public int rollTaskGroupCycle() {
//            CALLS.incrementAndGet();
//            return 0;
//        }
//
//        @Override
//        public VisitAgainSubtaskDto buildAgainVisitDraft(String orgid, Long inhospid, Long newTaskId, Long sendstate) {
//            CALLS.incrementAndGet();
//            return InhospVisitFacade.emptyAgainDraft("stub");
//        }
//
//        @Override
//        public boolean generatePublicitySubtask(Long taskid, Long patid, Long inhospid, int type, String config) {
//            CALLS.incrementAndGet();
//            return true;
//        }
//    }
//
//    /**
//     * æŽ¢é’ˆï¼šä»¥"旁观者"身份注入 {@code ObjectProvider<InhospVisitApi>},看容器按类型能找到谁。
//     * <p>
//     * å®ƒä¸æ˜¯ {@code InhospVisitApi} çš„实现,所以不存在"自引用被排除"的情况,
//     * å› æ­¤å®ƒçœ‹åˆ°çš„就是完整的候选集合 â€”— ç”¨æ¥è¯æ˜Ž"没有内部实现时容器会把门面自己当候选"。
//     */
//    public static class ProviderProbe {
//
//        @org.springframework.beans.factory.annotation.Autowired
//        private ObjectProvider<InhospVisitApi> provider;
//
//        InhospVisitApi rawCandidate() {
//            return provider.stream().findFirst().orElse(null);
//        }
//    }
//
//    // ======================================================================
//    // åŸºç¡€å‡ºå…¥é™¢ä¿¡æ¯æ•°æ®é€šè·¯ï¼ˆInhospVisitDataService)
//    //
//    // ä¸ºä»€ä¹ˆå¿…须单独验:
//    //   é—¨é¢åŒæ—¶å®žçŽ°äº† InhospVisitDataService,而 Spring çš„æž„造注入是按类型找候选。
//    //   æ—©å…ˆçš„写法(直接注入接口类型)在真实容器里会命中"正在创建的门面自己",
//    //   æŠ› BeanCurrentlyInCreationException â€”— è¿™ä¸ªç¼ºé™·ã€åªæœ‰çœŸèµ·å®¹å™¨ã€‘才能发现,
//    //   é™æ€æ£€æŸ¥ä¸Žç¼–译都发现不了(满意度模块踩过同类坑)。
//    //   å› æ­¤è¿™é‡Œç”¨æ¡©æ•°æ®æœåŠ¡æŠŠè¿™æ¡é€šè·¯é’‰ä½ï¼š
//    //     Â· é—¨é¢èƒ½è£…配(不自引用);
//    //     Â· æ•°æ®æ–¹æ³•确实转发到数据服务,且【不受 inhospvisit.enabled å¼€å…³ç®¡è¾–】。
//    // ======================================================================
//
//    /**
//     * é¡¶æ›¿ {@code DefaultInhospVisitDataService} çš„æ¡©ï¼šåªè®°è°ƒç”¨æ¬¡æ•°ï¼Œä¸ç¢°æ•°æ®åº“。
//     * <p>
//     * âš ï¸ æœ¬æ¡©åªå®žçŽ°æŽ¥å£ï¼ˆ<b>不继承</b> {@link DefaultInhospVisitDataService}):
//     * çœŸå®žå®žçŽ°ç±»å¸¦ä¸€ä¸ª {@code @Autowired PatMedInhospMapper} å­—段,继承它就会把
//     * "需要数据源"这个耦合带进本校验,而本校验的整个前提就是<b>不起数据库</b>
//     * ï¼ˆæžç®€ä¸Šä¸‹æ–‡é‡Œæ²¡æœ‰ Mapper Bean,继承版会在 refresh æ—¶æŠ› NoSuchBeanDefinitionException)。
//     * <p>
//     * é—¨é¢å› æ­¤æŒ‰ã€æŽ¥å£ã€‘{@code ObjectProvider<InhospVisitDataService>} å–候选并过滤自己,
//     * æ—¢èƒ½å–到本桩,也不会自引用/循环依赖。
//     */
//    public static class StubDataService implements InhospVisitDataService {
//
//        static final AtomicInteger CALLS = new AtomicInteger();
//
//        @Override
//        public com.ruoyi.inhospvisit.domain.PatMedInhosp selectPatMedInhospByInhospid(Long inhospid) {
//            CALLS.incrementAndGet();
//            com.ruoyi.inhospvisit.domain.PatMedInhosp row = new com.ruoyi.inhospvisit.domain.PatMedInhosp();
//            row.setInhospid(inhospid);
//            return row;
//        }
//
//        @Override
//        public int updatePatMedInhosp(com.ruoyi.inhospvisit.domain.PatMedInhosp patMedInhosp) {
//            CALLS.incrementAndGet();
//            return 7;
//        }
//
//        // ---- å…¶ä½™æ–¹æ³•:本校验不涉及,返回安全空值 ----
//
//        @Override
//        public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhosp(
//                com.ruoyi.inhospvisit.domain.PatMedInhosp q) {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectOutInhospByPatnoAndOutDate(
//                String patno, java.util.Date dayStart, java.util.Date dayEnd) {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public String getTagnameBypatid(Long patid) {
//            return null;
//        }
//
//        @Override
//        public java.util.List<java.util.Map<String, Object>> getTagnameByPatids(java.util.List<Long> patids) {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhospList(
//                com.ruoyi.inhospvisit.domain.PatMedInhosp q) {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhospListBySerialnum(
//                com.ruoyi.inhospvisit.domain.PatMedInhosp q) {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectPatMedInhospListByCondition(
//                com.ruoyi.inhospvisit.domain.PatMedInhospVO vo) {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public com.ruoyi.common.core.service.dto.PatMedRes selectPatMedInhospCount(
//                com.ruoyi.common.core.service.dto.PatMedReq req) {
//            return null;
//        }
//
//        @Override
//        public java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> selectNeedProcessList() {
//            return java.util.Collections.emptyList();
//        }
//
//        @Override
//        public int insertPatMedInhosp(com.ruoyi.inhospvisit.domain.PatMedInhosp p) {
//            return 0;
//        }
//
//        @Override
//        public int insertPatMedInhospBatch(java.util.List<com.ruoyi.inhospvisit.domain.PatMedInhosp> list) {
//            return 0;
//        }
//
//        @Override
//        public int deletePatMedInhospByInhospid(Long inhospid) {
//            return 0;
//        }
//
//        @Override
//        public int deletePatMedInhospByInhospno(String inhospno) {
//            return 0;
//        }
//
//        @Override
//        public int deletePatMedInhospByInhospids(Long[] inhospids) {
//            return 0;
//        }
//    }
//}
ruoyi-inhospvisit/src/test/java/com/ruoyi/inhospvisit/config/InhospVisitSwitchCheck.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,176 @@
package com.ruoyi.inhospvisit.config;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.inhospvisit.api.InhospVisitFacade;
import java.util.List;
/**
 * å‡ºå…¥é™¢éšè®¿æ¨¡å—开关校验(不依赖 JUnit,可直接用模块 classpath è¿è¡Œï¼‰ã€‚
 *
 * <h3>为什么必须做这个</h3>
 * æ¨¡å—原本有个 {@code InhospVisitContextCheck},但那份文件<b>整个被注释掉了</b>(412 è¡Œå…¨æ˜¯ {@code //}),
 * æ‰€ä»¥æ¨¡å—实际上<b>没有任何可运行的开关校验</b> â€”— è¿™æ˜¯æœ¬è½®å®¡è®¡æ˜Žç¡®ç‚¹å‡ºçš„缺口。
 * <p>
 * ç…§ {@code TemplateSwitchCheck} çš„形状写,验证的是"**接上了没有**",不只是"方法能不能返回 boolean":
 * <ol>
 *   <li><b>参数表优先</b>:{@code sys_config} æœ‰å€¼æ—¶ä»¥å®ƒä¸ºå‡†ï¼ŒåŒä¸€ä»½ yml é…ç½®ä¸‹å¯å¼€å¯å…³ï¼›</li>
 *   <li><b>yml å…œåº•</b>:{@code sys_config} æ²¡æœ‰è¿™ä¸€è¡Œæ—¶å›žè½ {@code inhospvisit.enabled}(默认 true);</li>
 *   <li><b>热更新</b>:改 {@code sys_config} åŽç¼“存过期即生效(无需重启);</li>
 *   <li><b>拦截器真的拦</b>:关闭时 {@code preHandle} æŠ›æ˜Žç¡®ä¸šåŠ¡é”™è¯¯ï¼›å¯ç”¨æ—¶æ”¾è¡Œï¼›</li>
 *   <li><b>受管端点</b>:恰好 15 ä¸ªï¼Œä¸” <b>包含</b>用户实机报过"关闭后仍能查"的
 *       {@code /smartor/patinhosp/selectPatMedInhospList}。</li>
 * </ol>
 */
public final class InhospVisitSwitchCheck {
    private static int failed = 0;
    private static final String SYS_ON = "true";
    private static final String SYS_OFF = "false";
    private static final String SYS_MISSING = null;
    private InhospVisitSwitchCheck() {
    }
    public static void main(String[] args) {
        System.out.println("=== 1) å‚数表优先 / yml å…œåº• ===");
        InhospVisitProperties ymlOn = new InhospVisitProperties();   // é»˜è®¤ true
        check("sys_config ç¼ºå¤± + yml é»˜è®¤ -> å¯ç”¨ï¼ˆfail-open)", "true",
                String.valueOf(new InhospVisitSwitch(ymlOn, SYS_MISSING).isEnabled()));
        check("sys_config=false è¦†ç›– yml é»˜è®¤ -> å…³é—­", "false",
                String.valueOf(new InhospVisitSwitch(ymlOn, SYS_OFF).isEnabled()));
        InhospVisitProperties ymlOff = new InhospVisitProperties();
        ymlOff.setEnabled(false);
        check("sys_config=true è¦†ç›– yml=false -> å¯ç”¨ï¼ˆå‚数表优先)", "true",
                String.valueOf(new InhospVisitSwitch(ymlOff, SYS_ON).isEnabled()));
        check("sys_config ç¼ºå¤± + yml=false -> å…³é—­ï¼ˆå…œåº•生效)", "false",
                String.valueOf(new InhospVisitSwitch(ymlOff, SYS_MISSING).isEnabled()));
        System.out.println();
        System.out.println("=== 2) éž true/false çš„脏值按「不是 false å³å¯ç”¨ã€å¤„理 ===");
        check("值 = \"0\" -> å¯ç”¨ï¼ˆä¸å› è„å€¼è¯¯å…³æ¨¡å—)", "true",
                String.valueOf(new InhospVisitSwitch(ymlOn, "0").isEnabled()));
        check("值 = \"FALSE\"(大写)-> å…³é—­", "false",
                String.valueOf(new InhospVisitSwitch(ymlOn, "FALSE").isEnabled()));
        System.out.println();
        System.out.println("=== 3) çƒ­æ›´æ–°ï¼ˆ10 ç§’缓存 + invalidateCache)===");
        final String[] value = {SYS_OFF};
        InhospVisitSwitch hot = hotSwitch(ymlOn, value);
        check("改之前(参数表=false)", "false", String.valueOf(hot.isEnabled()));
        value[0] = SYS_ON;
        check("缓存未过期时仍返回旧值(进程内 10 ç§’缓存)", "false", String.valueOf(hot.isEnabled()));
        hot.invalidateCache();
        check("缓存失效后读到新值(改成 true ç«‹å³ç”Ÿæ•ˆï¼‰", "true", String.valueOf(hot.isEnabled()));
        System.out.println();
        System.out.println("=== 4) æ‹¦æˆªå™¨ï¼šå…³é—­æ—¶æ˜Žç¡®æŠ¥é”™ï¼Œå¯ç”¨æ—¶æ”¾è¡Œ ===");
        InhospVisitAvailabilityInterceptor offInterceptor =
                new InhospVisitAvailabilityInterceptor(new InhospVisitSwitch(ymlOn, SYS_OFF));
        InhospVisitAvailabilityInterceptor onInterceptor =
                new InhospVisitAvailabilityInterceptor(new InhospVisitSwitch(ymlOn, SYS_ON));
        check("启用时 preHandle æ”¾è¡Œï¼ˆrequest ä¼  null ä¹Ÿä¸ NPE)", "true",
                String.valueOf(onInterceptor.preHandle(null, null, null)));
        String thrown = "(未抛异常)";
        try {
            offInterceptor.preHandle(null, null, null);
        } catch (Exception e) {
            thrown = e.getClass().getName() + "|" + e.getMessage();
        }
        check("关闭时 preHandle æŠ› ServiceException", "true",
                String.valueOf(thrown.startsWith("com.ruoyi.common.exception.ServiceException|")));
        check("关闭时错误提示明确(含 inhospvisit.enabled=false ä¸Žã€ŒæŽ¥å£ä¸å¯ç”¨ã€ï¼‰", "true",
                String.valueOf(thrown.contains("inhospvisit.enabled=false") && thrown.contains("接口不可用")));
        System.out.println();
        System.out.println("=== 5) å—管范围:前缀通配(新加接口自动生效)===");
        List<String> guarded = InhospVisitWebConfig.guardedPathPatterns();
        check("受管模式只有 1 æ¡ï¼ˆå‰ç¼€é€šé…ï¼Œä¸å†é€ä¸ªæžšä¸¾ï¼‰", "1", String.valueOf(guarded.size()));
        check("受管模式 = /smartor/patinhosp/**", "true",
                String.valueOf(guarded.contains("/smartor/patinhosp/**")));
        // å‰ç¼€é€šé…çš„语义:本模块前缀下**所有**端点都受管 â€”— åŒ…括用户实机报过"关了还能查"的那个
        String prefix = InhospVisitWebConfig.MODULE_URL_PREFIX;
        for (String ep : new String[]{
                "/smartor/patinhosp/selectPatMedInhospList",
                "/smartor/patinhosp/selectPatMedInhospListCount",
                "/smartor/patinhosp/export",
                "/smartor/patinhosp/add",
                "/smartor/patinhosp/dealOutHospInfo",
                "/smartor/patinhosp/addOperationSubTask"}) {
            check("被前缀覆盖:" + ep, "true", String.valueOf(ep.startsWith(prefix + "/")));
        }
        // âš ï¸ é€šé…çš„代价必须被显式承认:它不再能"静态枚举出管了哪些端点"。
        //    çœŸå®žç«¯ç‚¹æ¸…单由 sql/_probe/verify_inhospvisit_switch.py ä»ŽæŽ§åˆ¶å™¨æºç å®žæµ‹æ ¸å¯¹ã€‚
        check("前缀常量与通配模式一致(防止两者漂移)", "true",
                String.valueOf((prefix + "/**").equals(guarded.get(0))));
        System.out.println();
        System.out.println("=== 6) é—¨é¢ï¼šisEnabled() è‡ªèº«ä¸å¸¦å®ˆå«ï¼ˆå¦åˆ™é—®ä¸å‡ºå¼€å…³çŠ¶æ€ï¼‰===");
        // âš ï¸ InhospVisitFacade æ²¡æœ‰ 2 å‚构造器:测试构造是
        //    (properties, InhospVisitApi delegate, String sysConfigValue)。
        //    delegate ä¼  null è¡¨ç¤º"模块没被打进应用",此时 isEnabled() ä¸º false
        //    ï¼ˆå®ƒåˆ¤çš„æ˜¯"开关打开 **且** å†…部实现在位")—— è¿™ç‚¹ä¸Žæ¨¡æ¿é—¨é¢ä¸åŒï¼Œæ•…分别断言。
        InhospVisitFacade facadeOff = new InhospVisitFacade(ymlOn, null, SYS_OFF);
        check("关闭 + æ— å†…部实现时 isEnabled() = false", "false",
                String.valueOf(facadeOff.isEnabled()));
        InhospVisitFacade facadeOnNoImpl = new InhospVisitFacade(ymlOn, null, SYS_ON);
        check("开启但无内部实现时 isEnabled() = false(判的是开关+实现在位)", "false",
                String.valueOf(facadeOnNoImpl.isEnabled()));
        System.out.println();
        if (failed == 0) {
            System.out.println("===== å‡ºå…¥é™¢éšè®¿å¼€å…³æ ¡éªŒå…¨éƒ¨é€šè¿‡ =====");
        } else {
            System.out.println("===== å‡ºå…¥é™¢éšè®¿å¼€å…³æ ¡éªŒå¤±è´¥ " + failed + " é¡¹ =====");
            System.exit(1);
        }
    }
    /**
     * é€ ä¸€ä¸ªã€Œ{@code sys_config} çš„值可以被中途改掉」的开关,用于验证缓存与热更新。
     */
    private static InhospVisitSwitch hotSwitch(InhospVisitProperties properties, final String[] configValue) {
        return new InhospVisitSwitch(properties,
                new org.springframework.beans.factory.ObjectProvider<IConfigService>() {
                    private final IConfigService service = configKey -> configValue[0];
                    @Override
                    public IConfigService getObject(Object... args) {
                        return service;
                    }
                    @Override
                    public IConfigService getIfAvailable() {
                        return service;
                    }
                    @Override
                    public IConfigService getIfUnique() {
                        return service;
                    }
                    @Override
                    public IConfigService getObject() {
                        return service;
                    }
                    @Override
                    public java.util.stream.Stream<IConfigService> stream() {
                        return java.util.stream.Stream.of(service);
                    }
                });
    }
    private static void check(String key, String expected, String actual) {
        boolean ok = expected.equals(actual);
        if (!ok) {
            failed++;
        }
        System.out.println((ok ? "[OK]   " : "[FAIL] ") + key + "  æœŸæœ›=" + expected + "  å®žé™…=" + actual);
    }
}
ruoyi-inhospvisit/src/test/resources/logback-test.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
  ä»…用于 ruoyi-inhospvisit æ¨¡å—çš„ main() è‡ªæ£€ç¨‹åºï¼ˆInhospVisitContextCheck):
  æŠŠ Spring çš„ DEBUG åˆ·å±åŽ‹æŽ‰ï¼Œåªä¿ç•™ WARN ä»¥ä¸Šä¸Žè‡ªæ£€è¾“出,便于人工核对断言结果。
  ç”Ÿäº§è¿è¡Œä¸èµ°è¿™é‡Œï¼ˆç”Ÿäº§ç”¨çš„æ˜¯ ruoyi-admin/src/main/resources/logback*.xml)。
-->
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%-5level %logger{0} - %msg%n</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>
    <logger name="org.springframework" level="WARN"/>
    <logger name="com.ruoyi.inhospvisit" level="WARN"/>
    <root level="WARN">
        <appender-ref ref="STDOUT"/>
    </root>
</configuration>
ruoyi-othervisit/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
<?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-othervisit</artifactId>
    <description>
        å…¶ä»–随访模块:手术随访编排(扫描未处理手术记录 â†’ æŒ‰æ‰‹æœ¯ç¼–码匹配长期随访任务 â†’ å»ºå­ä»»åŠ¡ï¼‰çš„å”¯ä¸€å®žçŽ°å¤„ã€‚
        è¿ç§»å¯¹åº” PatMedOperationServiceImpl#dealOperationInfo(@ b25c3b51)。
        ä¾èµ–方向(务必保持):
          æœ¬æ¨¡å—【只依赖 ruoyi-common】,绝不依赖 smartor。
          â€”— å¦åˆ™ smartor å·²ä¾èµ–本模块,会形成循环依赖,Maven ç›´æŽ¥æž„建失败。
          è·¨æ¨¡å—能力一律走 ruoyi-common é‡Œä¸‹æ²‰çš„æŽ¥å£ï¼š
            Â· ServiceTaskMatchApi   â€”— é•¿æœŸéšè®¿ä»»åŠ¡"匹配"(手术编码维度)
            Â· VisitTaskCreateApi    â€”— å­ä»»åŠ¡çš„æŸ¥/停/增
            Â· ArchiveReadApi        â€”— æ‚£è€…档案只读
          è¿™äº›æŽ¥å£åœ¨ smartor ä¾§éƒ½æœ‰è¿‡æ¸¡å®žçŽ°ï¼Œå°†æ¥ã€Œéšè®¿ä»»åŠ¡åˆ›å»ºæ¨¡å—ã€æŽ¥ç®¡æ—¶æ¢å®žçŽ°æ–¹å³å¯ã€‚
        æ”¯æŒé€šè¿‡ sys_config å‚数表里的 othervisit.enabled æ•´ä½“关闭(运行时读取,改完最多 10 ç§’生效)。
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·ï¼ˆåŒæ—¶ä¼ é€’带入 mybatis / swagger-annotations / commons-lang3 / jackson ç­‰ï¼‰ -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <!--
            âš ï¸ åˆ»æ„ã€ä¸ã€‘依赖 ruoyi-quartz:
            ruoyi-quartz ä¾èµ– smartor,而 smartor ä¾èµ–本模块 â€”— å¼•入它等于间接依赖 smartor。
            æœ¬æ¨¡å—不写定时任务(定时入口在 RyTask,属调用方)。
        -->
        <!-- lombok:显式声明版本,避免注解处理器缺失 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.34</version>
        </dependency>
    </dependencies>
</project>
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/OtherVisitModule.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package com.ruoyi.othervisit;
/**
 * å…¶ä»–随访模块包根。
 * <p>
 * ç›®å½•约定(与 {@code ruoyi-inhospvisit} / {@code ruoyi-publicity} /
 * {@code ruoyi-outpatientvisit} å®Œå…¨ä¸€è‡´ï¼‰ï¼š
 * <ul>
 *   <li>{@code com.ruoyi.othervisit.api} â€”— <b>唯一对外包</b>;</li>
 *   <li>{@code service} / {@code domain} / {@code mapper} / {@code config} / {@code controller}
 *       â€”— æ¨¡å—内部实现。</li>
 * </ul>
 *
 * <h3>本模块负责什么</h3>
 * <b>手术随访</b>编排:扫描未处理的手术记录({@code pat_med_operation})→ æŒ‰<b>手术编码</b>
 * ï¼ˆ{@code opcode})匹配长期随访任务 â†’ åŒ¹é…ä¸åˆ°å°±ç½®æ ‡å¿—位并写原因,匹配到就按
 * {@code writeInSubTask} å»ºå­ä»»åŠ¡ã€‚è¿ç§»å¯¹åº”
 * {@code PatMedOperationServiceImpl#dealOperationInfo}(@ b25c3b51)。
 *
 * <p>
 * åå­—叫"其他随访"而不是"手术随访",是为了给除出入院/门急诊/宣教之外的**其它随访类型**
 * ç•™ä½ç½®ï¼›å½“前实现主体就是手术随访。
 *
 * <p>
 * æ¨¡å—可通过 {@code sys_config} å‚数表的 {@code othervisit.enabled=false} æ•´ä½“关闭
 * ï¼ˆè¿è¡Œæ—¶è¯»å–,改完最多 10 ç§’生效)。
 */
public final class OtherVisitModule {
    /**
     * æ¨¡å—配置前缀(仅作为 yml å…œåº•配置的键前缀;主开关在 sys_config å‚数表里)
     */
    public static final String PROPERTIES_PREFIX = "othervisit";
    /**
     * sys_config å‚数表里的开关键名
     */
    public static final String SWITCH_KEY = "othervisit.enabled";
    private OtherVisitModule() {
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/api/OtherVisitApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,116 @@
package com.ruoyi.othervisit.api;
import com.ruoyi.common.core.service.OtherVisitApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
 * {@link OtherVisitApi} çš„<b>本模块实现</b>。
 *
 * <h3>它取代了什么</h3>
 * åœ¨ {@code ruoyi-othervisit} å»ºå¥½ä¹‹å‰ï¼Œ{@link OtherVisitApi} åªæœ‰ smartor ä¾§çš„
 * è¿‡æ¸¡æ¡¥æŽ¥ {@code OtherVisitApiBridge}。手术随访的实现搬进本模块后,
 * ç”±æœ¬ç±»æŽ¥ç®¡ â€”— è¿™æ­£æ˜¯é‚£ä¸ª Bridge çš„类注释里预告的"将来动作"。
 *
 * <h3>⚠️ æœ¬ç±»ä¸¤ä¸ªæ–¹æ³•<b>本次都还委托 smartor</b>(刻意,不是漏做)</h3>
 * <table border="1">
 *   <tr><th>接口方法</th><th>原实现位置</th><th>为什么本次不搬</th></tr>
 *   <tr><td>{@link #addOperationSubTask(String)}</td>
 *       <td>{@code PatMedInhospServiceImpl#addOperationSubTask} L1247-1316</td>
 *       <td>它是<b>手术等级({@code oplevelcode})+ ç¦»é™¢ç§‘室/病区维度</b>的扫描,
 *           å±ž"出院随访里混着的手术分支",且依赖
 *           {@code ServiceTaskoperMapper}(仍在 smartor)与
 *           {@code writeInSubTaskForOp}(依赖 {@code ScheduleSubtaskBuilder})。</td></tr>
 *   <tr><td>{@link #dealOutHospInfoBySchedule(String)}</td>
 *       <td>同文件 L380-614</td>
 *       <td>它的<b>非手术分支</b>与"HIS è‡ªåŠ¨çº³å…¥"、"前端手动勾选"共用
 *           {@code ScheduleSubtaskBuilder}。搬它得连 builder ä¸€èµ·æ¬ â€”—
 *           é‚£æ˜¯ç‹¬ç«‹çš„一次改造。</td></tr>
 * </table>
 *
 * <h3>那本模块搬了什么?</h3>
 * <b>手术编码({@code opcode})维度</b>的那条扫描:
 * {@code PatMedOperationServiceImpl#dealOperationInfo}(原 L107-149)。
 * å®ƒçš„入口<b>不走</b>本接口,而是 {@link OtherVisitFacade#dealOperationInfo()} â€”—
 * è°ƒç”¨æ–¹æ˜¯ {@code RyTask#dealOperationInfo}(定时任务),已直接改走门面。
 * ä¹‹æ‰€ä»¥ä¸å¡žè¿›æœ¬æŽ¥å£ï¼Œæ˜¯å› ä¸ºä¸¤è€…语义不同:
 * æŽ¥å£ä¸Šé‚£ä¸¤ä¸ªæ˜¯"出院/任务组流程里的手术分支",而这条是"手术记录自身的扫描"。
 *
 * <h3>⚠️ ä¸ºä»€ä¹ˆç¼–译期看不到 smartor</h3>
 * ä¾èµ–方向是 smartor â†’ ruoyi-othervisit,反过来会形成 Maven å¾ªçŽ¯ä¾èµ–ï¼Œç›´æŽ¥æž„å»ºå¤±è´¥ã€‚
 * æ‰€ä»¥è¿™é‡Œç”¨ {@code ObjectProvider<OtherVisitApi>} åœ¨<b>运行时</b>按类型惰性查找
 * smartor ä¾§é‚£ä¸ªæ‰¿æŽ¥å®žçŽ°ï¼Œå¹¶<b>排除本类自己</b>(否则会自己调自己 â†’ {@code StackOverflowError})。
 * å–不到就打 WARN å¹¶è¿”回安全值,绝不让定时任务抛异常。
 *
 * @see OtherVisitApi
 */
@Slf4j
@Component
public class OtherVisitApiImpl implements OtherVisitApi {
    /**
     * smartor ä¾§çš„过渡承接({@code com.smartor.service.impl.bridge.OtherVisitApiBridge})。
     * <p>
     * âš ï¸ ç±»åž‹å‚数刻意写成 {@link OtherVisitApi} è€Œ<b>不是</b>那个具体类:
     * æœ¬æ¨¡å—编译期看不到 smartor çš„类型(也不允许看到)。
     */
    @Autowired
    private ObjectProvider<OtherVisitApi> otherVisitApiProvider;
    /**
     * æ‰‹æœ¯éšè®¿æ‰«æï¼ˆæ‰‹æœ¯ç­‰çº§ + ç¦»é™¢ç§‘室/病区维度)—— æœ¬æ¬¡ä»å§”托 smartor。
     *
     * @param config {@code sys_config: visit.early.day} çš„值,可能为 null
     * @return å¤„理的手术记录数;承接实现不可用时返回 0
     */
    @Override
    public int addOperationSubTask(String config) {
        OtherVisitApi delegate = findExternalDelegate();
        if (delegate == null) {
            log.warn("【手术随访】addOperationSubTask çš„ smartor ä¾§æ‰¿æŽ¥å®žçŽ°ä¸å¯ç”¨ï¼ˆæœªæ‰“è¿›åº”ç”¨æˆ–æœªè£…é…ï¼‰ï¼Œæœ¬æ¬¡è·³è¿‡ã€‚");
            return 0;
        }
        return delegate.addOperationSubTask(config);
    }
    /**
     * ä»»åŠ¡ç»„ï¼ˆappltype=5)首轮纳入 â€”— æœ¬æ¬¡ä»å§”托 smartor。
     *
     * @param config {@code sys_config: visit.early.day}
     * @return åŽŸå®žçŽ°å›ºå®šè¿”å›ž 1;承接实现不可用时返回 0
     */
    @Override
    public int dealOutHospInfoBySchedule(String config) {
        OtherVisitApi delegate = findExternalDelegate();
        if (delegate == null) {
            log.warn("【手术随访】dealOutHospInfoBySchedule çš„ smartor ä¾§æ‰¿æŽ¥å®žçŽ°ä¸å¯ç”¨ï¼ˆæœªæ‰“è¿›åº”ç”¨æˆ–æœªè£…é…ï¼‰ï¼Œæœ¬æ¬¡è·³è¿‡ã€‚");
            return 0;
        }
        return delegate.dealOutHospInfoBySchedule(config);
    }
    /**
     * æ‰¾ smartor ä¾§é‚£ä¸ªæ‰¿æŽ¥å®žçŽ°ã€‚
     * <p>
     * âš ï¸ <b>必须排除本类</b>:本类也实现了 {@link OtherVisitApi},
     * è‹¥ä¸è¿‡æ»¤å°±ä¼šå–到自己,形成无限递归。
     * âš ï¸ ç”¨ {@code stream()} è€Œä¸æ˜¯ {@code getIfAvailable()}:
     * åŽè€…在存在多个候选时行为不确定,而这里天然有<b>两个</b>候选实现(本类 + smartor æ¡¥æŽ¥ï¼‰ã€‚
     */
    private OtherVisitApi findExternalDelegate() {
        if (otherVisitApiProvider == null) {
            return null;
        }
        try {
            return otherVisitApiProvider.stream()
                    .filter(api -> api != this)
                    .findFirst()
                    .orElse(null);
        } catch (Exception e) {
            log.warn("【手术随访】获取其它随访承接实现失败,按不可用处理:{}", e.toString());
            return null;
        }
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/api/OtherVisitFacade.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,337 @@
package com.ruoyi.othervisit.api;
import com.ruoyi.othervisit.config.OtherVisitProperties;
import com.ruoyi.othervisit.config.OtherVisitSwitch;
import com.ruoyi.othervisit.domain.BaseDictOperation;
import com.ruoyi.othervisit.domain.PatMedOperation;
import com.ruoyi.othervisit.domain.PatMedOperationItem;
import com.ruoyi.othervisit.service.BaseDictOperationService;
import com.ruoyi.othervisit.service.OperationItemService;
import com.ruoyi.othervisit.service.OperationService;
import com.ruoyi.othervisit.service.OperationVisitTaskService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * å…¶ä»–随访(手术随访)模块对外门面(<b>常驻 Bean</b>)。
 *
 * <h3>这是其它模块访问手术数据的<b>唯一入口</b></h3>
 * æŒ‰ä½ å®šçš„跨模块数据访问原则:<b>别的模块要用手术数据,只能从这个模块调用,
 * è€Œä¸æ˜¯åœ¨è‡ªå·±æ¨¡å—里写 SQL</b>。所以本门面包了两类能力:
 * <ol>
 *   <li><b>随访编排</b>:{@link #dealOperationInfo()};</li>
 *   <li><b>基础数据读写</b>:{@code pat_med_operation} / {@code pat_med_operation_item} /
 *       {@code base_dict_operation} çš„增删改查。</li>
 * </ol>
 * ç›¸åº”地,下面的调用方都必须改走本门面(本次一并改完):
 * <ul>
 *   <li>{@code ruoyi-quartz} çš„ {@code RyTask#dealOperationInfo} ä¸Ž {@code CollectHISServiceImpl};</li>
 *   <li>smartor çš„ {@code ServiceExternalServiceImpl}(HIS å¤–部手术回传)、
 *       {@code PatMedInhospServiceImpl}(出入院随访的手术分支)、
 *       {@code ServiceSubtaskServiceImpl}。</li>
 * </ul>
 *
 * <h3>为什么用常驻 Bean(不用 {@code @ConditionalOnProperty})</h3>
 * ä¸Ž {@code InhospVisitFacade} / {@code PublicityFacade} / {@code OuthospVisitFacade} åŒä¸€å¥—设计:
 * <ul>
 *   <li><b>永远存在</b> â€”— è°ƒç”¨æ–¹ï¼ˆ{@code RyTask} å®šæ—¶ä»»åŠ¡ã€{@code @Autowired})在任何开关状态下
 *       éƒ½<b>不会启动失败</b>;</li>
 *   <li>开关在 {@code sys_config} çš„ {@code othervisit.enabled},由
 *       {@link OtherVisitSwitch} <b>运行时</b>读取(10 ç§’进程内缓存),改完无需重启;</li>
 *   <li>关闭时返回安全值并打 WARN,<b>不抛异常</b>,保证调用方主流程照常完成。</li>
 * </ul>
 *
 * <h3>⚠️ å“ªäº›æ–¹æ³•<b>不</b>受开关管辖</h3>
 * ä¸‹é¢æ‰€æœ‰ã€ŒåŸºç¡€æ•°æ®è¯»å†™ã€æ–¹æ³•都<b>不</b>判 {@code othervisit.enabled},
 * ç†ç”±ä¸Žå‡ºå…¥é™¢/门诊模块一致:手术记录是 HIS é‡‡é›†å†™å…¥çš„原始业务数据,
 * å…³æŽ‰"生成随访"不该让 HIS é‡‡é›†å†™ä¸è¿›æ‰‹æœ¯è¡¨ â€”— é‚£æ˜¯æ•°æ®æ•…障,与"今天不发随访"不是一回事。
 * åªæœ‰ {@link #dealOperationInfo()} å—开关管辖。
 */
@Slf4j
@Component
public class OtherVisitFacade {
    private final OtherVisitProperties properties;
    /**
     * è¿è¡Œæ—¶å¼€å…³ï¼ˆ{@code sys_config} ä¼˜å…ˆï¼Œ{@link OtherVisitProperties} å…œåº•)
     */
    private final OtherVisitSwitch otherVisitSwitch;
    /**
     * æ‰‹æœ¯éšè®¿ç¼–排(扫描未处理手术记录 â†’ åŒ¹é…é•¿æœŸä»»åŠ¡ â†’ å†™å­ä»»åŠ¡ï¼‰ã€‚
     * <p>
     * âš ï¸ ç”¨ {@code ObjectProvider} æƒ°æ€§å–:容器里若没有它也不该让门面装配失败。
     */
    private final ObjectProvider<OperationVisitTaskService> taskServiceProvider;
    /**
     * æ‰‹æœ¯è®°å½•基础读写(本模块自己的组件,没有条件装配,故直接注入)。
     */
    private final OperationService operationService;
    /**
     * æ‰‹æœ¯æ˜Žç»†åŸºç¡€è¯»å†™ã€‚
     */
    private final OperationItemService operationItemService;
    /**
     * æ‰‹æœ¯åŸºç¡€ä¿¡æ¯è¯»å†™ã€‚
     */
    private final BaseDictOperationService baseDictOperationService;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器
     * ï¼ˆå¦ä¸€ä¸ªæ˜¯ç»™æµ‹è¯•/非 Spring çŽ¯å¢ƒç”¨çš„ï¼‰ï¼Œè‹¥ä¸æ ‡æ³¨ï¼ŒSpring ä¼šå› ä¸º"构造器不唯一"
     * è€Œé€€å›žæŸ¥æ‰¾æ— å‚构造器,从而在启动时报 {@code No default constructor found}。
     * â€”— è¿™æ˜¯æ»¡æ„åº¦æ¨¡å—<b>实机踩到</b>的缺陷,此处刻意保留同样的写法与注释。
     */
    @Autowired
    public OtherVisitFacade(OtherVisitProperties properties,
                            OtherVisitSwitch otherVisitSwitch,
                            ObjectProvider<OperationVisitTaskService> taskServiceProvider,
                            OperationService operationService,
                            OperationItemService operationItemService,
                            BaseDictOperationService baseDictOperationService) {
        this.properties = properties;
        this.otherVisitSwitch = otherVisitSwitch;
        this.taskServiceProvider = taskServiceProvider;
        this.operationService = operationService;
        this.operationItemService = operationItemService;
        this.baseDictOperationService = baseDictOperationService;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造。
     * <p>
     * æ‰€æœ‰æ•°æ®æœåŠ¡ä¸Žç¼–æŽ’æœåŠ¡éƒ½ä¸ä¼ ï¼šæµ‹è¯•æž„é€ ä¸‹è°ƒç”¨å®ƒä»¬ä¼šæ‰“ WARN å¹¶å®‰å…¨è¿”回,
     * ä¿è¯å¼€å…³æ ¡éªŒç±»æµ‹è¯•不必拉起数据库。
     */
    public OtherVisitFacade(OtherVisitProperties properties, String sysConfigValue) {
        this(properties, new OtherVisitSwitch(properties, sysConfigValue), null, null, null, null);
    }
    /**
     * å–运行时开关(供测试断言 / æ‹¦æˆªå™¨å¤ç”¨ï¼‰
     */
    public OtherVisitSwitch getOtherVisitSwitch() {
        return otherVisitSwitch;
    }
    /**
     * æ‰‹æœ¯éšè®¿æ¨¡å—是否可用(开关打开)
     */
    public boolean isEnabled() {
        return otherVisitSwitch.isEnabled();
    }
    // ==========================================================================
    // éšè®¿ç¼–排(受开关管辖)
    // ==========================================================================
    /**
     * æ‰‹æœ¯éšè®¿ä¸»æ‰«æï¼ˆä¾› {@code RyTask#dealOperationInfo} è°ƒç”¨ï¼‰ã€‚
     *
     * <h3>⚠️ æœ¬æ–¹æ³•<b>受</b> {@code othervisit.enabled} ç®¡è¾–</h3>
     * å®ƒæ˜¯"生成手术随访"的业务动作 â€”— å…³æŽ‰å¼€å…³å°±è¯¥åœä¸‹ã€‚
     * å…³é—­æ—¶è¿”回 {@code 0} å¹¶æ‰“ WARN(不抛异常,保证定时任务主流程不被拖垮)。
     *
     * <h3>返回值说明</h3>
     * æ­£å¸¸æ—¶è¿”回编排结果(原实现固定返回 1,<b>不是</b>处理条数);
     * å…³é—­æˆ–组件不可用时返回 0。原调用方 {@code RyTask} å¿½ç•¥è¿”回值,
     * æ‰€ä»¥è¿™ä¸ªçº¦å®šåªå½±å“æ—¥å¿—与将来的排查。
     */
    public Integer dealOperationInfo() {
        if (!isEnabled()) {
            log.warn("【手术随访】模块未启用(参数配置 othervisit.enabled=false),dealOperationInfo è·³è¿‡ã€‚");
            return 0;
        }
        OperationVisitTaskService svc = taskService();
        if (svc == null) {
            log.warn("【手术随访】容器里没有 OperationVisitTaskService(或非 Spring æž„造),dealOperationInfo è·³è¿‡ã€‚");
            return 0;
        }
        return svc.dealOperationInfo();
    }
    // ==========================================================================
    // æ‰‹æœ¯è®°å½•(pat_med_operation)—— ä¸å—开关管辖
    // ==========================================================================
    /**
     * æŸ¥è¯¢æ‰‹æœ¯è®°å½•。
     * <p>对应原 {@code IPatMedOperationService#selectPatMedOperationById}。
     */
    public PatMedOperation selectOperationById(Long id) {
        return operationService == null ? null : operationService.selectById(id);
    }
    /**
     * æŸ¥è¯¢æ‰‹æœ¯è®°å½•列表。
     * <p>对应原 {@code IPatMedOperationService#selectPatMedOperationList}。
     */
    public List<PatMedOperation> selectOperationList(PatMedOperation query) {
        return operationService == null ? java.util.Collections.emptyList() : operationService.selectList(query);
    }
    /**
     * æ–°å¢žæ‰‹æœ¯è®°å½•。
     * <p>对应原 {@code IPatMedOperationService#insertPatMedOperation}。
     */
    public int insertOperation(PatMedOperation row) {
        return operationService == null ? 0 : operationService.insert(row);
    }
    /**
     * ä¿®æ”¹æ‰‹æœ¯è®°å½•。
     * <p>对应原 {@code IPatMedOperationService#updatePatMedOperation}。
     */
    public int updateOperation(PatMedOperation row) {
        return operationService == null ? 0 : operationService.update(row);
    }
    /**
     * æ‰¹é‡åˆ é™¤æ‰‹æœ¯è®°å½•。
     * <p>对应原 {@code IPatMedOperationService#deletePatMedOperationByIds}。
     */
    public int deleteOperationByIds(Long[] ids) {
        return operationService == null ? 0 : operationService.deleteByIds(ids);
    }
    /**
     * åˆ é™¤æ‰‹æœ¯è®°å½•。
     * <p>对应原 {@code IPatMedOperationService#deletePatMedOperationById}。
     */
    public int deleteOperationById(Long id) {
        return operationService == null ? 0 : operationService.deleteById(id);
    }
    // ==========================================================================
    // æ‰‹æœ¯æ˜Žç»†ï¼ˆpat_med_operation_item)—— ä¸å—开关管辖
    // ==========================================================================
    public PatMedOperationItem selectOperationItemById(Long id) {
        return operationItemService == null ? null : operationItemService.selectById(id);
    }
    public List<PatMedOperationItem> selectOperationItemList(PatMedOperationItem query) {
        return operationItemService == null ? java.util.Collections.emptyList() : operationItemService.selectList(query);
    }
    /**
     * æŒ‰æ¡ä»¶æŸ¥æ‰‹æœ¯æ˜Žç»†ï¼ˆ{@code JOIN pat_med_inhosp} çš„那条)。
     * <p>
     * âš ï¸ ä¸Ž {@link #selectOperationItemList(PatMedOperationItem)} <b>不是同一条 SQL</b>,别混用。
     */
    public List<PatMedOperationItem> selectOperationItemListByCondition(PatMedOperationItem query) {
        return operationItemService == null ? java.util.Collections.emptyList()
                : operationItemService.selectOperationItemList(query);
    }
    /**
     * æŒ‰æ¡ä»¶æŸ¥æ‰‹æœ¯æ˜Žç»†ï¼ŒåŒä¸€ {@code opid} ä¸‹åªå– {@code oplevelcode} æœ€å¤§çš„一条。
     * <p>调用方:出入院随访的手术分支({@code PatMedInhospServiceImpl})。
     */
    public List<PatMedOperationItem> selectOperationItemListMaxLevel(PatMedOperationItem query) {
        return operationItemService == null ? java.util.Collections.emptyList()
                : operationItemService.selectOperationItemListMaxLevel(query);
    }
    public int insertOperationItem(PatMedOperationItem row) {
        return operationItemService == null ? 0 : operationItemService.insert(row);
    }
    public int insertOperationItems(List<PatMedOperationItem> rows) {
        return operationItemService == null ? 0 : operationItemService.insertBatch(rows);
    }
    public int updateOperationItem(PatMedOperationItem row) {
        return operationItemService == null ? 0 : operationItemService.update(row);
    }
    /**
     * æŒ‰ {@code operationid} æ›´æ–°æ‰‹æœ¯æ˜Žç»†ã€‚
     * <p>⚠️ ä¸ä»£è®¾ {@code updateTime}(原直调 Mapper çš„调用方自己负责)。
     */
    public int updateOperationItemByOperationid(PatMedOperationItem row) {
        return operationItemService == null ? 0 : operationItemService.updateByOperationid(row);
    }
    public int deleteOperationItemByIds(Long[] ids) {
        return operationItemService == null ? 0 : operationItemService.deleteByIds(ids);
    }
    public int deleteOperationItemById(Long id) {
        return operationItemService == null ? 0 : operationItemService.deleteById(id);
    }
    /**
     * æŒ‰ {@code opid} <b>物理</b>删除手术明细。
     */
    public int deleteHardOperationItemByOpId(String opid) {
        return operationItemService == null ? 0 : operationItemService.deleteHardByOpId(opid);
    }
    /**
     * æŒ‰ {@code opid} <b>软</b>删除手术明细。
     */
    public int deleteSoftOperationItemByOpId(String opid) {
        return operationItemService == null ? 0 : operationItemService.deleteSoftByOpId(opid);
    }
    /**
     * æŒ‰ {@code operationid} åˆ é™¤æ‰‹æœ¯æ˜Žç»†ã€‚
     */
    public int deleteOperationItemByOperationid(Long operationid) {
        return operationItemService == null ? 0 : operationItemService.deleteByOperationid(operationid);
    }
    // ==========================================================================
    // æ‰‹æœ¯åŸºç¡€ä¿¡æ¯ï¼ˆbase_dict_operation)—— ä¸å—开关管辖
    // ==========================================================================
    public BaseDictOperation selectBaseDictOperationById(Long id) {
        return baseDictOperationService == null ? null : baseDictOperationService.selectById(id);
    }
    public List<BaseDictOperation> selectBaseDictOperationList(BaseDictOperation query) {
        return baseDictOperationService == null ? java.util.Collections.emptyList()
                : baseDictOperationService.selectList(query);
    }
    public int insertBaseDictOperation(BaseDictOperation row) {
        return baseDictOperationService == null ? 0 : baseDictOperationService.insert(row);
    }
    public int updateBaseDictOperation(BaseDictOperation row) {
        return baseDictOperationService == null ? 0 : baseDictOperationService.update(row);
    }
    public int deleteBaseDictOperationByIds(Long[] ids) {
        return baseDictOperationService == null ? 0 : baseDictOperationService.deleteByIds(ids);
    }
    public int deleteBaseDictOperationById(Long id) {
        return baseDictOperationService == null ? 0 : baseDictOperationService.deleteById(id);
    }
    // ==========================================================================
    private OperationVisitTaskService taskService() {
        if (taskServiceProvider == null) {
            return null;
        }
        try {
            return taskServiceProvider.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【手术随访】获取 OperationVisitTaskService å¤±è´¥ï¼ŒæŒ‰ä¸å¯ç”¨å¤„理:{}", e.toString());
            return null;
        }
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/config/OtherVisitProperties.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package com.ruoyi.othervisit.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
 * å…¶ä»–随访模块的 yml é…ç½®ï¼ˆ<b>仅作兜底,不是主开关</b>)。
 *
 * <h3>⚠️ ä¸»å¼€å…³ä¸åœ¨è¿™é‡Œ</h3>
 * ä¸»å¼€å…³æ˜¯ {@code sys_config} å‚数配置表里的 {@code othervisit.enabled},
 * ç”± {@link OtherVisitSwitch} <b>运行时热读</b>(进程内 10 ç§’缓存,改完最多 10 ç§’生效)。
 * æœ¬ç±»åªåœ¨ã€Œsys_config é‡Œæ²¡æœ‰é‚£ä¸€è¡Œã€æˆ–「非 Spring çŽ¯å¢ƒå–ä¸åˆ°é…ç½®æœåŠ¡ã€æ—¶å…œåº•ã€‚
 *
 * <h3>为什么默认 true</h3>
 * æ‰‹æœ¯éšè®¿æ˜¯ä¸»åŠ›ä¸šåŠ¡ï¼šå…³æŽ‰ = å½“天手术患者的随访不再生成。
 * è¦å…³é—­è¯·åˆ°ã€Œç³»ç»Ÿç®¡ç† â†’ å‚数设置」显式把 {@code othervisit.enabled} è®¾ä¸º false。
 */
@Data
@Component
@ConfigurationProperties(prefix = "othervisit")
public class OtherVisitProperties {
    /**
     * yml å…œåº•开关(默认 true)
     */
    private boolean enabled = true;
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/config/OtherVisitSwitch.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,172 @@
package com.ruoyi.othervisit.config;
import com.ruoyi.common.core.service.IConfigService;
import com.ruoyi.othervisit.OtherVisitModule;
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>
 * å‚数键名:othervisit.enabled
 * å‚数键值:true  = å¯ç”¨ï¼ˆé»˜è®¤ï¼‰
 *          false = å…³é—­ï¼ˆæ‰‹æœ¯éšè®¿ä¸å†ç”Ÿæˆå­ä»»åŠ¡ï¼‰
 * </pre>
 *
 * <h3>与其它模块的开关对齐</h3>
 * æœ¬ç±»ä¸Ž {@code InhospVisitSwitch} / {@code PublicitySwitch} / {@code OuthospVisitSwitch}
 * æ˜¯åŒä¸€å¥—实现(10 ç§’进程内缓存、fail-open、运行时热读),刻意保持形状一致。
 *
 * <h3>为什么读不到配置也按 true(fail-open)</h3>
 * è¯» {@code sys_config} è¦èµ°æ•°æ®åº“,数据库抖动时 fail-closed ä¼šè®©å½“天手术随访整体停摆;
 * è¦åœéšè®¿çš„æ­£ç¡®åšæ³•是显式把参数设为 false(或到「定时任务」里暂停对应 job)。
 */
@Slf4j
@Component
public class OtherVisitSwitch {
    /**
     * sys_config é‡Œçš„参数键名(与 {@link OtherVisitModule#SWITCH_KEY} ä¿æŒä¸€è‡´ï¼‰
     */
    public static final String CONFIG_KEY = OtherVisitModule.SWITCH_KEY;
    /**
     * è¿›ç¨‹å†…缓存有效期(毫秒)
     */
    private static final long CACHE_TTL_MILLIS = 10_000L;
    private final OtherVisitProperties properties;
    private final ObjectProvider<IConfigService> configServiceProvider;
    private volatile Boolean cachedEnabled;
    private volatile long cachedAt;
    /**
     * Spring ä½¿ç”¨çš„æž„造器。
     * <p>
     * âš ï¸ <b>必须显式标注 {@code @Autowired}</b>:本类有两个公开构造器,
     * ä¸æ ‡æ³¨çš„话 Spring ä¼šå› "构造器不唯一"退回查找无参构造器,启动即报
     * {@code No default constructor found}。—— è¿™æ˜¯æ»¡æ„åº¦æ¨¡å—实机踩到的坑。
     */
    @Autowired
    public OtherVisitSwitch(OtherVisitProperties properties,
                            ObjectProvider<IConfigService> configServiceProvider) {
        this.properties = properties;
        this.configServiceProvider = configServiceProvider;
    }
    /**
     * ä¾›<b>测试/非 Spring çŽ¯å¢ƒ</b>直接构造:{@code configValue} å°±æ˜¯ sys_config é‡Œçš„键值,
     * ä¼  {@code null} è¡¨ç¤º"参数行不存在"(此时回落到 {@link OtherVisitProperties})。
     */
    public OtherVisitSwitch(OtherVisitProperties properties, final String configValue) {
        this(properties, new FixedProvider(configValue));
        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;
    }
    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 = properties.isEnabled();
        log.debug("【其他随访】sys_config ä¸­æœªé…ç½® {},回落 othervisit.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) {
            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-othervisit/src/main/java/com/ruoyi/othervisit/controller/BaseDictOperationController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
package com.ruoyi.othervisit.controller;
import java.util.List;
import com.ruoyi.common.annotation.AddOrgId;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.othervisit.domain.BaseDictOperation;
import com.ruoyi.othervisit.api.OtherVisitFacade;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
 * BaseDictOperationController â€”— ä»Ž {@code ruoyi-admin} çš„ {@code BaseDictOperationController.java} <b>整体迁入</b>。
 *
 * <h3>⚠️ URL ä¸€ä¸ªéƒ½ä¸èƒ½å˜</h3>
 * ç±»çº§è·¯å¾„与方法级路径逐字照抄,由
 * {@code sql/_probe/verify_othervisit_url_equivalence.py} æŠŠå…³ï¼ˆå…¨é›†åˆ¤å®š + é‡å¤æ˜ å°„检查)。
 *
 * <h3>迁入时同步删除</h3>
 * åŽŸ {@code BaseDictOperationController.java} å¿…须整体删除,否则两个类都映射同一路径,
 * Spring ä¼šå›  {@code Ambiguous mapping} <b>直接启动失败</b>。
 *
 * <h3>与原实现唯一的一处改写(已登记)</h3>
 * ä¸å†ç» smartor ä¾§é‚£ä¸ªå·²åˆ é™¤çš„ Service æŽ¥å£ï¼ˆcom.smartor.service.IBaseDictOperationService):
 * å®ƒåŽŸæœ¬åªè¢«æœ¬ Controller ä½¿ç”¨ï¼Œå±žçº¯ CRUD,已整体删除;
 * æœ¬ç±»æ”¹ä¸ºç»æœ¬æ¨¡å—门面 {@link OtherVisitFacade} è°ƒç”¨
 * {@code com.ruoyi.othervisit.service.*Service}。方法体、注解、分页方式均未改。
 *
 * <h3>开关</h3>
 * æœ¬ç±»<b>不挂</b>开关拦截器:属基础数据 CRUD(前端管理页在用),
 * {@code othervisit.enabled} åªè¯¥æŽ§åˆ¶"要不要生成手术随访"。
 *
 * @author lihu
 * @date 2025-02-26
 */
@Api("手术基础信息")
@RestController
@RequestMapping("/smartor/baseopera")
public class BaseDictOperationController extends BaseController {
    @Autowired
    private OtherVisitFacade facade;
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子列表
     */
    @ApiOperation("查询患者检查检验记录子列表")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:list')")
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("list")
    public TableDataInfo list(@RequestBody BaseDictOperation baseDictOperation) {
        startPage();
        LoginUser loginUser = getLoginUser();
        SysUser user = loginUser.getUser();
        baseDictOperation.setOrgid(user.getOrgid());
        List<BaseDictOperation> list = facade.selectBaseDictOperationList(baseDictOperation);
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºæ‚£è€…检查检验记录子列表
     */
    @ApiOperation("导出患者检查检验记录子列表")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:export')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, BaseDictOperation baseDictOperation) {
        List<BaseDictOperation> list = facade.selectBaseDictOperationList(baseDictOperation);
        ExcelUtil<BaseDictOperation> util = new ExcelUtil<BaseDictOperation>(BaseDictOperation.class);
        util.exportExcel(response, list, "患者检查检验记录子数据");
    }
    /**
     * èŽ·å–æ‚£è€…æ£€æŸ¥æ£€éªŒè®°å½•å­è¯¦ç»†ä¿¡æ¯
     */
    @ApiOperation("获取患者检查检验记录子详细信息")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:query')")
    @GetMapping(value = "/getInfo/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(facade.selectBaseDictOperationById(id));
    }
    /**
     * æ–°å¢žæ‚£è€…检查检验记录子
     */
    @ApiOperation("新增患者检查检验记录子")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:add')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.INSERT)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
@PostMapping("/add")
    public AjaxResult add(@RequestBody BaseDictOperation baseDictOperation) {
        return toAjax(facade.insertBaseDictOperation(baseDictOperation));
    }
    /**
     * ä¿®æ”¹æ‚£è€…检查检验记录子
     */
    @ApiOperation("修改患者检查检验记录子")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:edit')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.UPDATE)
@AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
@PostMapping("/edit")
    public AjaxResult edit(@RequestBody BaseDictOperation baseDictOperation) {
        return toAjax(facade.updateBaseDictOperation(baseDictOperation));
    }
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     */
    @ApiOperation("删除患者检查检验记录子")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:remove')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.DELETE)
    @GetMapping("/remove/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(facade.deleteBaseDictOperationByIds(ids));
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/controller/OperationController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,133 @@
package com.ruoyi.othervisit.controller;
import java.util.List;
import com.ruoyi.common.annotation.AddOrgId;
import com.ruoyi.common.utils.PageUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.othervisit.domain.PatMedOperation;
import com.ruoyi.othervisit.api.OtherVisitFacade;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
 * OperationController â€”— ä»Ž {@code ruoyi-admin} çš„ {@code PatMedOperationController.java} <b>整体迁入</b>。
 *
 * <h3>⚠️ URL ä¸€ä¸ªéƒ½ä¸èƒ½å˜</h3>
 * ç±»çº§è·¯å¾„与方法级路径逐字照抄,由
 * {@code sql/_probe/verify_othervisit_url_equivalence.py} æŠŠå…³ï¼ˆå…¨é›†åˆ¤å®š + é‡å¤æ˜ å°„检查)。
 *
 * <h3>迁入时同步删除</h3>
 * åŽŸ {@code PatMedOperationController.java} å¿…须整体删除,否则两个类都映射同一路径,
 * Spring ä¼šå›  {@code Ambiguous mapping} <b>直接启动失败</b>。
 *
 * <h3>与原实现唯一的一处改写(已登记)</h3>
 * ä¸å†ç» smartor ä¾§é‚£ä¸ªå·²åˆ é™¤çš„ Service æŽ¥å£ï¼ˆcom.smartor.service.IPatMedOperationService):
 * å®ƒåŽŸæœ¬åªè¢«æœ¬ Controller ä½¿ç”¨ï¼Œå±žçº¯ CRUD,已整体删除;
 * æœ¬ç±»æ”¹ä¸ºç»æœ¬æ¨¡å—门面 {@link OtherVisitFacade} è°ƒç”¨
 * {@code com.ruoyi.othervisit.service.*Service}。方法体、注解、分页方式均未改。
 *
 * <h3>开关</h3>
 * æœ¬ç±»<b>不挂</b>开关拦截器:属基础数据 CRUD(前端管理页在用),
 * {@code othervisit.enabled} åªè¯¥æŽ§åˆ¶"要不要生成手术随访"。
 *
 * @author lihu
 * @date 2025-02-25
 */
@Api("患者手术记录")
@RestController
@RequestMapping("/smartor/operation")
public class OperationController extends BaseController {
    @Autowired
    private OtherVisitFacade facade;
    /**
     * æŸ¥è¯¢æ‚£è€…手术记录列表
     */
    @ApiOperation("查询患者手术记录列表")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:list')")
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("list")
    public TableDataInfo list(@RequestBody PatMedOperation patMedOperation) {
        //startPage();
        PageUtils.startPageByPost(patMedOperation.getPageNum(), patMedOperation.getPageSize());
        List<PatMedOperation> list = facade.selectOperationList(patMedOperation);
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºæ‚£è€…手术记录列表
     */
    @ApiOperation("导出患者手术记录列表")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:export')")
    @Log(title = "患者手术记录", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, PatMedOperation patMedOperation) {
        List<PatMedOperation> list = facade.selectOperationList(patMedOperation);
        ExcelUtil<PatMedOperation> util = new ExcelUtil<PatMedOperation>(PatMedOperation.class);
        util.exportExcel(response, list, "患者手术记录数据");
    }
    /**
     * èŽ·å–æ‚£è€…æ‰‹æœ¯è®°å½•è¯¦ç»†ä¿¡æ¯
     */
    @ApiOperation("获取患者手术记录详细信息")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:query')")
    @GetMapping(value = "/getInfo/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(facade.selectOperationById(id));
    }
    /**
     * æ–°å¢žæ‚£è€…手术记录
     */
    @ApiOperation("新增患者手术记录")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:add')")
    @Log(title = "患者手术记录", businessType = BusinessType.INSERT)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/add")
    public AjaxResult add(@RequestBody PatMedOperation patMedOperation) {
        return toAjax(facade.insertOperation(patMedOperation));
    }
    /**
     * ä¿®æ”¹æ‚£è€…手术记录
     */
    @ApiOperation("修改患者手术记录")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:edit')")
    @Log(title = "患者手术记录", businessType = BusinessType.UPDATE)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/edit")
    public AjaxResult edit(@RequestBody PatMedOperation patMedOperation) {
        return toAjax(facade.updateOperation(patMedOperation));
    }
    /**
     * åˆ é™¤æ‚£è€…手术记录
     */
    @ApiOperation("删除患者手术记录")
    //@PreAuthorize("@ss.hasPermi('smartor:operation:remove')")
    @Log(title = "患者手术记录", businessType = BusinessType.DELETE)
    @GetMapping("/remove/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(facade.deleteOperationByIds(ids));
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/controller/OperationItemController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,130 @@
package com.ruoyi.othervisit.controller;
import java.util.List;
import com.ruoyi.common.annotation.AddOrgId;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.servlet.http.HttpServletResponse;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.common.annotation.Log;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.othervisit.domain.PatMedOperationItem;
import com.ruoyi.othervisit.api.OtherVisitFacade;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.common.core.page.TableDataInfo;
/**
 * OperationItemController â€”— ä»Ž {@code ruoyi-admin} çš„ {@code PatMedOperationItemController.java} <b>整体迁入</b>。
 *
 * <h3>⚠️ URL ä¸€ä¸ªéƒ½ä¸èƒ½å˜</h3>
 * ç±»çº§è·¯å¾„与方法级路径逐字照抄,由
 * {@code sql/_probe/verify_othervisit_url_equivalence.py} æŠŠå…³ï¼ˆå…¨é›†åˆ¤å®š + é‡å¤æ˜ å°„检查)。
 *
 * <h3>迁入时同步删除</h3>
 * åŽŸ {@code PatMedOperationItemController.java} å¿…须整体删除,否则两个类都映射同一路径,
 * Spring ä¼šå›  {@code Ambiguous mapping} <b>直接启动失败</b>。
 *
 * <h3>与原实现唯一的一处改写(已登记)</h3>
 * ä¸å†ç» smartor ä¾§é‚£ä¸ªå·²åˆ é™¤çš„ Service æŽ¥å£ï¼ˆcom.smartor.service.IPatMedOperationItemService):
 * å®ƒåŽŸæœ¬åªè¢«æœ¬ Controller ä½¿ç”¨ï¼Œå±žçº¯ CRUD,已整体删除;
 * æœ¬ç±»æ”¹ä¸ºç»æœ¬æ¨¡å—门面 {@link OtherVisitFacade} è°ƒç”¨
 * {@code com.ruoyi.othervisit.service.*Service}。方法体、注解、分页方式均未改。
 *
 * <h3>开关</h3>
 * æœ¬ç±»<b>不挂</b>开关拦截器:属基础数据 CRUD(前端管理页在用),
 * {@code othervisit.enabled} åªè¯¥æŽ§åˆ¶"要不要生成手术随访"。
 *
 * @author lihu
 * @date 2025-02-25
 */
@Api("患者检查检验记录子")
@RestController
@RequestMapping("/smartor/item")
public class OperationItemController extends BaseController {
    @Autowired
    private OtherVisitFacade facade;
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子列表
     */
    @ApiOperation("查询患者检查检验记录子列表")
    //@PreAuthorize("@ss.hasPermi('smartor:item:list')")
    @GetMapping("/list")
    public TableDataInfo list(PatMedOperationItem patMedOperationItem) {
        startPage();
        List<PatMedOperationItem> list = facade.selectOperationItemList(patMedOperationItem);
        return getDataTable(list);
    }
    /**
     * å¯¼å‡ºæ‚£è€…检查检验记录子列表
     */
    @ApiOperation("导出患者检查检验记录子列表")
    //@PreAuthorize("@ss.hasPermi('smartor:item:export')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(HttpServletResponse response, PatMedOperationItem patMedOperationItem) {
        List<PatMedOperationItem> list = facade.selectOperationItemList(patMedOperationItem);
        ExcelUtil<PatMedOperationItem> util = new ExcelUtil<PatMedOperationItem>(PatMedOperationItem.class);
        util.exportExcel(response, list, "患者检查检验记录子数据");
    }
    /**
     * èŽ·å–æ‚£è€…æ£€æŸ¥æ£€éªŒè®°å½•å­è¯¦ç»†ä¿¡æ¯
     */
    @ApiOperation("获取患者检查检验记录子详细信息")
    //@PreAuthorize("@ss.hasPermi('smartor:item:query')")
    @GetMapping(value = "/getInfo/{id}")
    public AjaxResult getInfo(@PathVariable("id") Long id) {
        return success(facade.selectOperationItemById(id));
    }
    /**
     * æ–°å¢žæ‚£è€…检查检验记录子
     */
    @ApiOperation("新增患者检查检验记录子")
    //@PreAuthorize("@ss.hasPermi('smartor:item:add')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.INSERT)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/add")
    public AjaxResult add(@RequestBody PatMedOperationItem patMedOperationItem) {
        return toAjax(facade.insertOperationItem(patMedOperationItem));
    }
    /**
     * ä¿®æ”¹æ‚£è€…检查检验记录子
     */
    @ApiOperation("修改患者检查检验记录子")
    //@PreAuthorize("@ss.hasPermi('smartor:item:edit')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.UPDATE)
    @AddOrgId(field = "orgid", paramIndex = 0, campusField = "campusid")
    @PostMapping("/edit")
    public AjaxResult edit(@RequestBody PatMedOperationItem patMedOperationItem) {
        return toAjax(facade.updateOperationItem(patMedOperationItem));
    }
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     */
    @ApiOperation("删除患者检查检验记录子")
    //@PreAuthorize("@ss.hasPermi('smartor:item:remove')")
    @Log(title = "患者检查检验记录子", businessType = BusinessType.DELETE)
    @GetMapping("/remove/{ids}")
    public AjaxResult remove(@PathVariable Long[] ids) {
        return toAjax(facade.deleteOperationItemByIds(ids));
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/domain/BaseDictOperation.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
package com.ruoyi.othervisit.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.util.Date;
/**
 * æ‚£è€…检查检验记录子对象 base_dict_operation
 *
 * @author lihu
 * @date 2025-02-26
 */
@Data
@ApiModel("患者检查检验记录子")
public class BaseDictOperation extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty("自增ID")
    //数据库自增改成@TableId(type = IdType.AUTO)
    private Long id;
    /**
     * his手术id
     */
    @ApiModelProperty("his手术id")
    @Excel(name = "his手术id")
    private String opid;
    /**
     * æ‰‹æœ¯åç§°ID
     */
    @ApiModelProperty("手术名称ID")
    @Excel(name = "手术名称ID")
    private String opcode;
    /**
     * æ‰‹æœ¯åç§°
     */
    @ApiModelProperty("手术名称")
    @Excel(name = "手术名称")
    private String opdesc;
    /**
     * åˆ‡å£ç±»åˆ«ä»£ç 
     */
    @ApiModelProperty("切口类别代码")
    @Excel(name = "切口类别代码")
    private String incitypecode;
    /**
     * åˆ‡å£ç±»åˆ«åç§°
     */
    @ApiModelProperty("切口类别名称")
    @Excel(name = "切口类别名称")
    private String incitypedesc;
    /**
     * æ‰‹æœ¯çº§åˆ«ä»£ç 
     */
    @ApiModelProperty("手术级别代码")
    @Excel(name = "手术级别代码")
    private String oplevelcode;
    /**
     * æ‰‹æœ¯çº§åˆ«åç§°
     */
    @ApiModelProperty("手术级别名称")
    @Excel(name = "手术级别名称")
    private String opleveldesc;
    /**
     * æ‰‹æœ¯éƒ¨ä½
     */
    @ApiModelProperty("手术部位")
    @Excel(name = "手术部位")
    private String opposition;
    /**
     * åˆ é™¤æ ‡å¿—(0:未删除 1:已删除)
     */
    @ApiModelProperty("删除标志(0:未删除 1:已删除)")
    private String delFlag;
    /**
     * ä¸Šä¼ æ ‡å¿—(0:未上传 1:已上传)
     */
    @ApiModelProperty("上传标志(0:未上传 1:已上传)")
    @Excel(name = "上传标志", readConverterExp = "0=:未上传,1=:已上传")
    private Long isupload;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    @ApiModelProperty("上传时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "上传时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date uploadTime;
    /**
     * åŒ»é™¢æœºæž„ID
     */
    @ApiModelProperty("医院机构ID")
    @Excel(name = "医院机构ID")
    private String orgid;
    /**
     * çˆ¶GUID
     */
    @ApiModelProperty("父GUID")
    @Excel(name = "父GUID")
    private String pguid;
    /**
     * GUID
     */
    @ApiModelProperty("GUID")
    @Excel(name = "GUID")
    private String guid;
    /**
     * çˆ¶ID
     */
    @ApiModelProperty("父ID")
    @Excel(name = "父ID")
    private Long pid;
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/domain/PatMedOperation.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,551 @@
package com.ruoyi.othervisit.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.util.Date;
/**
 * æ‚£è€…手术记录对象 pat_med_operation
 *
 * @author lihu
 * @date 2025-02-25
 */
@Data
@ApiModel("患者手术记录")
public class PatMedOperation extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty("自增ID")
    //数据库自增改成@TableId(type = IdType.AUTO)
    private Long id;
    /**
     * ç—…人ID
     */
    @ApiModelProperty("病人ID")
    @Excel(name = "病人ID")
    private Long patid;
    /**
     * his系统ID
     */
    @ApiModelProperty("his系统ID")
    private String patidHis;
    /**
     * ç—…人编号
     */
    @ApiModelProperty("病人编号")
    @Excel(name = "病人编号")
    private String patcode;
    /**
     * ç—…人姓名
     */
    @ApiModelProperty("病人姓名")
    @Excel(name = "病人姓名")
    private String paname;
    /**
     * æ‰‹æœ¯ç”³è¯·å•ID
     */
    @ApiModelProperty("手术申请单ID")
    @Excel(name = "手术申请单ID")
    private Long opreqid;
    /**
     * æ‰‹æœ¯ç”³è¯·å•号
     */
    @ApiModelProperty("手术申请单号")
    @Excel(name = "手术申请单号")
    private String opreqcode;
    /**
     * æ‰‹æœ¯çŠ¶æ€ï¼šN-申请R-安排D-拒绝
     * C-取消 F-结束
     */
    @ApiModelProperty("手术状态:N-申请R-安排D-拒绝 C-取消 F-结束")
    @Excel(name = "手术状态:N-申请R-安排D-拒绝 C-取消 F-结束")
    private String opsstatus;
    /**
     * æ‰‹æœ¯ç±»åˆ«ä»£ç 
     */
    @ApiModelProperty("手术类别代码")
    @Excel(name = "手术类别代码")
    private String optypecode;
    /**
     * æ‰‹æœ¯ç±»åˆ«åç§°
     */
    @ApiModelProperty("手术类别名称")
    @Excel(name = "手术类别名称")
    private String optypename;
    /**
     * è¦æ±‚手术时间
     */
    @ApiModelProperty("要求手术时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "要求手术时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date operatortime;
    /**
     * æ‰‹æœ¯ç”³è¯·æ—¶é—´
     */
    @ApiModelProperty("手术申请时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "手术申请时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date opreqtime;
    /**
     * ç”³è¯·åŒ»ç”Ÿç¼–号
     */
    @ApiModelProperty("申请医生编号")
    @Excel(name = "申请医生编号")
    private String reqdoccode;
    /**
     * ç”³è¯·åŒ»ç”Ÿå§“名
     */
    @ApiModelProperty("申请医生姓名")
    @Excel(name = "申请医生姓名")
    private String reqdocname;
    /**
     * ç”³è¯·ç§‘室编号
     */
    @ApiModelProperty("申请科室编号")
    @Excel(name = "申请科室编号")
    private String reqdeptcode;
    /**
     * ç”³è¯·ç§‘室名称
     */
    @ApiModelProperty("申请科室名称")
    @Excel(name = "申请科室名称")
    private String reqdeptname;
    /**
     * æ‰‹æœ¯ç§‘室编号
     */
    @ApiModelProperty("手术科室编号")
    @Excel(name = "手术科室编号")
    private String reqloccode;
    /**
     * æ‰‹æœ¯ç§‘室名称
     */
    @ApiModelProperty("手术科室名称")
    @Excel(name = "手术科室名称")
    private String reqlocname;
    /**
     * ä¸»åˆ€åŒ»ç”Ÿç¼–号
     */
    @ApiModelProperty("主刀医生编号")
    @Excel(name = "主刀医生编号")
    private String surgeoncode;
    /**
     * ä¸»åˆ€åŒ»ç”Ÿå§“名
     */
    @ApiModelProperty("主刀医生姓名")
    @Excel(name = "主刀医生姓名")
    private String surgeonname;
    /**
     * ä¸€åŠ©åŒ»ç”Ÿç¼–å·
     */
    @ApiModelProperty("一助医生编号")
    @Excel(name = "一助医生编号")
    private String firasscode;
    /**
     * ä¸€åŠ©åŒ»ç”Ÿå§“å
     */
    @ApiModelProperty("一助医生姓名")
    @Excel(name = "一助医生姓名")
    private String firassname;
    /**
     * äºŒåŠ©åŒ»ç”Ÿç¼–å·
     */
    @ApiModelProperty("二助医生编号")
    @Excel(name = "二助医生编号")
    private String secasscode;
    /**
     * äºŒåŠ©åŒ»ç”Ÿå§“å
     */
    @ApiModelProperty("二助医生姓名")
    @Excel(name = "二助医生姓名")
    private String secassname;
    /**
     * ä¸‰åŠ©åŒ»ç”Ÿç¼–å·
     */
    @ApiModelProperty("三助医生编号")
    @Excel(name = "三助医生编号")
    private String thrasscode;
    /**
     * ä¸‰åŠ©åŒ»ç”Ÿå§“å
     */
    @ApiModelProperty("三助医生姓名")
    @Excel(name = "三助医生姓名")
    private String thrassname;
    /**
     * ä½œåºŸæ—¶é—´
     */
    @ApiModelProperty("作废时间")
    @Excel(name = "作废时间")
    private String canceltime;
    /**
     * ä½œåºŸåŽŸå› 
     */
    @ApiModelProperty("作废原因")
    @Excel(name = "作废原因")
    private String cancelreason;
    /**
     * ä½œåºŸç”³è¯·äººç¼–号
     */
    @ApiModelProperty("作废申请人编号")
    @Excel(name = "作废申请人编号")
    private String cancelcode;
    /**
     * ä½œåºŸç”³è¯·äººå§“名
     */
    @ApiModelProperty("作废申请人姓名")
    @Excel(name = "作废申请人姓名")
    private String cancelname;
    /**
     * å°±è¯Šç±»åž‹;1门诊 2住院 3体检
     */
    @ApiModelProperty("就诊类型;1门诊 2住院 3体检")
    @Excel(name = "就诊类型;1门诊 2住院 3体检")
    private Long visittype;
    /**
     * æ‰‹æœ¯çŠ¶æ€ï¼šN-申请R-安排D-拒绝
     * C-取消 F-结束
     */
    @ApiModelProperty("手术状态:N-申请R-安排D-拒绝 C-取消 F-结束")
    @Excel(name = "手术状态:N-申请R-安排D-拒绝 C-取消 F-结束")
    private String status;
    /**
     * æ–¹æ¡ˆçŠ¶æ€æ“ä½œæ—¶é—´
     */
    @ApiModelProperty("方案状态操作时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "方案状态操作时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date schemetime;
    /**
     * æ˜¯å¦ç”Ÿæˆè®¡åˆ’;0未生成 1生成手术前 2生成手术后  9无匹配方案
     */
    @ApiModelProperty("是否生成计划;0未生成 1生成手术前 2生成手术后  9无匹配方案")
    @Excel(name = "是否生成计划;0未生成 1生成手术前 2生成手术后  9无匹配方案")
    private Long schemestatus;
    /**
     * åˆ é™¤æ ‡å¿—(0:未删除 1:已删除)
     */
    @ApiModelProperty("删除标志(0:未删除 1:已删除)")
    private String delFlag;
    /**
     * ä¸Šä¼ æ ‡å¿—(0:未上传 1:已上传)
     */
    @ApiModelProperty("上传标志(0:未上传 1:已上传)")
    @Excel(name = "上传标志", readConverterExp = "0=:未上传,1=:已上传")
    private Long isupload;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    @ApiModelProperty("上传时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "上传时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date uploadTime;
    /**
     * åŒ»é™¢æœºæž„ID
     */
    @ApiModelProperty("医院机构ID")
    @Excel(name = "医院机构ID")
    private String orgid;
    /**
     * GUID
     */
    @ApiModelProperty("GUID")
    @Excel(name = "GUID")
    private String guid;
    /**
     * çˆ¶GUID
     */
    @ApiModelProperty("父GUID")
    @Excel(name = "父GUID")
    private String pguid;
    /**
     * çˆ¶ID
     */
    @ApiModelProperty("父ID")
    @Excel(name = "父ID")
    private Long pid;
    /**
     * å°±è¯Šç±»åž‹æ ‡è¯†ï¼š0:在院 1:出院 2:转科
     */
    @ApiModelProperty("就诊类型标识:0:在院 1:出院 2:转科")
    @Excel(name = "就诊类型标识:0:在院 1:出院 2:转科")
    private String inhospstate;
    /**
     * æ‚£è€…档案编号
     */
    @ApiModelProperty("患者档案编号")
    @Excel(name = "患者档案编号")
    private String patno;
    /**
     * ç—…人姓名
     */
    @ApiModelProperty("病人姓名")
    @Excel(name = "病人姓名")
    private String patname;
    /**
     * é—¨è¯Šå°±è¯ŠID
     */
    @ApiModelProperty("门诊就诊ID")
    @Excel(name = "门诊就诊ID")
    private Long visitid;
    /**
     * å°±è¯Šæµæ°´å·
     */
    @ApiModelProperty("就诊流水号")
    @Excel(name = "就诊流水号")
    private String serialnum;
    /**
     * ä½é™¢å·
     */
    @ApiModelProperty("住院号")
    @Excel(name = "住院号")
    private String inhospno;
    /**
     * åŒ»é™¢åç§°
     */
    @ApiModelProperty("医院名称")
    @Excel(name = "医院名称")
    private String hospitalname;
    /**
     * åŒ»é™¢ç¼–号
     */
    @ApiModelProperty("医院编号")
    @Excel(name = "医院编号")
    private String hospitalcode;
    /**
     * å°±è¯Šç§‘室ID
     */
    @ApiModelProperty("就诊科室ID")
    @Excel(name = "就诊科室ID")
    private String deptid;
    /**
     * ç§‘室代码
     */
    @ApiModelProperty("科室代码")
    @Excel(name = "科室代码")
    private String deptcode;
    /**
     * ç§‘室名称
     */
    @ApiModelProperty("科室名称")
    @Excel(name = "科室名称")
    private String deptname;
    /**
     * ç—…区编号
     */
    @ApiModelProperty("病区编号")
    @Excel(name = "病区编号")
    private String hospitaldistrictcode;
    /**
     * ç—…区名称
     */
    @ApiModelProperty("病区名称")
    @Excel(name = "病区名称")
    private String hospitaldistrictname;
    /**
     * å‡ºé™¢ç§‘室编码
     */
    @ApiModelProperty(value = "出院科室编码")
    private String leaveldeptcode;
    /**
     * å‡ºé™¢ç§‘室名称
     */
    @ApiModelProperty(value = "出院科室名称")
    private String leaveldeptname;
    /**
     * å‡ºé™¢ç—…区编码
     */
    @ApiModelProperty(value = "出院病区编码")
    private String leavehospitaldistrictcode;
    /**
     * å‡ºé™¢ç—…区名称
     */
    @ApiModelProperty(value = "出院病区名称")
    private String leavehospitaldistrictname;
    /**
     * æˆ¿é—´å·
     */
    @ApiModelProperty("房间号")
    @Excel(name = "房间号")
    private String roomno;
    /**
     * åºŠä½å·
     */
    @ApiModelProperty("床位号")
    @Excel(name = "床位号")
    private String bedNo;
    /**
     * ä¸»æ²»åŒ»ç”Ÿå·¥å·
     */
    @ApiModelProperty("主治医生工号")
    @Excel(name = "主治医生工号")
    private String drcode;
    /**
     * ä¸»æ²»åŒ»ç”Ÿåç§°
     */
    @ApiModelProperty("主治医生名称")
    @Excel(name = "主治医生名称")
    private String drname;
    /**
     * æŠ¤å£«ID
     */
    @ApiModelProperty("护士ID")
    @Excel(name = "护士ID")
    private String nurseId;
    /**
     * æŠ¤å£«å§“名
     */
    @ApiModelProperty("护士姓名")
    @Excel(name = "护士姓名")
    private String nurseName;
    /**
     * é•¿æœŸä»»åŠ¡ï¼Œæ˜¯å¦å¤„ç†ï¼š0未处理   1处理成功   2处理失败
     */
    @ApiModelProperty("长期任务,是否处理:0未处理   1处理成功   2处理失败")
    @Excel(name = "长期任务,是否处理:0未处理   1处理成功   2处理失败")
    private String checkFlag;
    /**
     * é•¿æœŸä»»åŠ¡ ï¼Œå¤±è´¥åŽŸå› 
     */
    @ApiModelProperty("长期任务 ï¼Œå¤±è´¥åŽŸå› ")
    @Excel(name = "长期任务 ï¼Œå¤±è´¥åŽŸå› ")
    private String longTaskReason;
    /**
     * æ‰‹æœ¯id
     */
    @ApiModelProperty("手术id")
    @Excel(name = "手术id")
    private String opid;
    /**
     * æ‰‹æœ¯åç§°ID
     */
    @ApiModelProperty("手术名称ID")
    @Excel(name = "手术名称ID")
    private String opcode;
    /**
     * æ‰‹æœ¯åç§°
     */
    @ApiModelProperty("手术名称")
    @Excel(name = "手术名称")
    private String opdesc;
    /**
     * æ‰‹æœ¯çº§åˆ«ä»£ç 
     */
    @ApiModelProperty("手术级别代码")
    @Excel(name = "手术级别代码")
    private String oplevelcode;
    /**
     * æ‰‹æœ¯çº§åˆ«åç§°
     */
    @ApiModelProperty("手术级别名称")
    @Excel(name = "手术级别名称")
    private String opleveldesc;
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "开始手术时间")
    private Date beginTime;
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "  ", width = 30, dateFormat = "yyyy-MM-dd")
    @ApiModelProperty(value = "结束手术时间")
    private Date endTime;
    @ApiModelProperty(value = "最后开始时间")
    private String lastStartTime;
    @ApiModelProperty(value = "最后结束时间")
    private String lastEndTime;
    /**
     * pageNum
     */
    @ApiModelProperty("pageNum")
    private Integer pageNum;
    /**
     * pageSize
     */
    @ApiModelProperty("pageSize")
    private Integer pageSize;
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/domain/PatMedOperationItem.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,271 @@
package com.ruoyi.othervisit.domain;
import com.fasterxml.jackson.annotation.JsonFormat;
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 java.util.Date;
import java.util.List;
/**
 * æ‚£è€…检查检验记录子对象 pat_med_operation_item
 *
 * @author lihu
 * @date 2025-02-25
 */
@Data
@ApiModel("患者检查检验记录子")
public class PatMedOperationItem extends BaseEntity {
    private static final long serialVersionUID = 1L;
    /**
     * è‡ªå¢žID
     */
    @ApiModelProperty("自增ID")
    //数据库自增改成@TableId(type = IdType.AUTO)
    private Long id;
    /**
     * æ‰‹æœ¯ç”³è¯·å•id
     */
    @ApiModelProperty("手术申请单id")
    @Excel(name = "手术申请单id")
    private Long operationid;
    /**
     * æ‰‹æœ¯id
     */
    @ApiModelProperty("手术id")
    @Excel(name = "手术id")
    private String opid;
    /**
     * æ‰‹æœ¯åç§°ID
     */
    @ApiModelProperty("手术名称ID")
    @Excel(name = "手术名称ID")
    private String opcode;
    /**
     * æ‰‹æœ¯åç§°
     */
    @ApiModelProperty("手术名称")
    @Excel(name = "手术名称")
    private String opdesc;
    /**
     * ä¸»æ‰‹æœ¯æ ‡è¯†
     */
    @ApiModelProperty("主手术标识")
    @Excel(name = "主手术标识")
    private String mainFlag;
    /**
     * åˆ‡å£ç±»åˆ«ä»£ç 
     */
    @ApiModelProperty("切口类别代码")
    @Excel(name = "切口类别代码")
    private String incitypecode;
    /**
     * åˆ‡å£ç±»åˆ«åç§°
     */
    @ApiModelProperty("切口类别名称")
    @Excel(name = "切口类别名称")
    private String incitypedesc;
    /**
     * æ‰‹æœ¯çº§åˆ«ä»£ç 
     */
    @ApiModelProperty("手术级别代码")
    @Excel(name = "手术级别代码")
    private String oplevelcode;
    /**
     * æ‰‹æœ¯çº§åˆ«åç§°
     */
    @ApiModelProperty("手术级别名称")
    @Excel(name = "手术级别名称")
    private String opleveldesc;
    /**
     * æ‰‹æœ¯éƒ¨ä½
     */
    @ApiModelProperty("手术部位")
    @Excel(name = "手术部位")
    private String opposition;
    /**
     * åˆ é™¤æ ‡å¿—(0:未删除 1:已删除)
     */
    @ApiModelProperty("删除标志(0:未删除 1:已删除)")
    private String delFlag;
    /**
     * ä¸Šä¼ æ ‡å¿—(0:未上传 1:已上传)
     */
    @ApiModelProperty("上传标志(0:未上传 1:已上传)")
    @Excel(name = "上传标志", readConverterExp = "0=:未上传,1=:已上传")
    private Long isupload;
    /**
     * ä¸Šä¼ æ—¶é—´
     */
    @ApiModelProperty("上传时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @Excel(name = "上传时间", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
    private Date uploadTime;
    /**
     * åŒ»é™¢æœºæž„ID
     */
    @ApiModelProperty("医院机构ID")
    @Excel(name = "医院机构ID")
    private String orgid;
    /**
     * çˆ¶GUID
     */
    @ApiModelProperty("父GUID")
    @Excel(name = "父GUID")
    private String pguid;
    /**
     * GUID
     */
    @ApiModelProperty("GUID")
    @Excel(name = "GUID")
    private String guid;
    /**
     * çˆ¶ID
     */
    @ApiModelProperty("父ID")
    @Excel(name = "父ID")
    private Long pid;
    @ApiModelProperty(value = "最后开始时间")
    private String lastStartTime;
    @ApiModelProperty(value = "最后结束时间")
    private String lastEndTime;
    /**
     * æ‰‹æœ¯é•¿æœŸä»»åŠ¡ï¼Œæ˜¯å¦å¤„ç†ï¼š0未处理   1处理
     */
    @ApiModelProperty(value = "手术长期任务,是否处理:0未处理   1处理成功    2处理失败 ")
    private String opercheckFlag;
    @ApiModelProperty("长期任务 ï¼Œå¤±è´¥åŽŸå› ")
    @Excel(name = "长期任务 ï¼Œå¤±è´¥åŽŸå› ")
    private String longTaskReason;
    /**
     * ç§‘室代码
     */
    @ApiModelProperty("科室代码")
    private String deptcode;
    /**
     * ç§‘室名称
     */
    @ApiModelProperty("科室名称")
    private String deptname;
    /**
     * ç—…区编号
     */
    @ApiModelProperty("病区编号")
    private String hospitaldistrictcode;
    /**
     * ç—…区名称
     */
    @ApiModelProperty("病区名称")
    private String hospitaldistrictname;
    @ApiModelProperty("患者ID")
    private Long patid;
    /**
     * ä¸»æ²»åŒ»ç”Ÿå·¥å·
     */
    @ApiModelProperty("主治医生工号")
    @Excel(name = "主治医生工号")
    private String drcode;
    /**
     * ä¸»æ²»åŒ»ç”Ÿåç§°
     */
    @ApiModelProperty("主治医生名称")
    @Excel(name = "主治医生名称")
    private String drname;
    /**
     * æŠ¤å£«ID
     */
    @ApiModelProperty("护士ID")
    @Excel(name = "护士ID")
    private String nurseId;
    /**
     * æŠ¤å£«å§“名
     */
    @ApiModelProperty("护士姓名")
    @Excel(name = "护士姓名")
    private String nurseName;
    @ApiModelProperty(value = "病区编码集合")
    private List<String> wardCodes;
    @ApiModelProperty(value = "部门编码集合")
    private List<String> deptCodes;
    @ApiModelProperty(value = "手术等级集合")
    private List<String> opLevelCodes;
    /**
     * å‡ºé™¢ç§‘室编码
     */
    @ApiModelProperty(value = "出院科室编码")
    private String leaveldeptcode;
    /**
     * å‡ºé™¢ç§‘室名称
     */
    @ApiModelProperty(value = "出院科室名称")
    private String leaveldeptname;
    /**
     * å‡ºé™¢ç—…区编码
     */
    @ApiModelProperty(value = "出院病区编码")
    private String leavehospitaldistrictcode;
    /**
     * å‡ºé™¢ç—…区名称
     */
    @ApiModelProperty(value = "出院病区名称")
    private String leavehospitaldistrictname;
    /**
     * ä½é™¢æ ‡è¯†ï¼š0:在院  1:出院    2:转科  3待入院
     */
    @ApiModelProperty(value = "住院标识: 0:在院  1:出院    2:转科  3待入院")
    private String inhospstate;
    /**
     * ä½é™¢è®°å½•流水号
     */
    @ApiModelProperty("住院记录流水号")
    @Excel(name = "住院记录流水号")
    private String serialnum;
    @ApiModelProperty(value = "出院时间")
    private String inhospEndtime;
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/mapper/BaseDictOperationMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package com.ruoyi.othervisit.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.ruoyi.othervisit.domain.BaseDictOperation;
/**
 * æ‚£è€…检查检验记录子Mapper接口
 *
 * @author lihu
 * @date 2025-02-26
 */
@Mapper
public interface BaseDictOperationMapper
{
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子
     *
     * @param id æ‚£è€…检查检验记录子主键
     * @return æ‚£è€…检查检验记录子
     */
    public BaseDictOperation selectBaseDictOperationById(Long id);
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子列表
     *
     * @param baseDictOperation æ‚£è€…检查检验记录子
     * @return æ‚£è€…检查检验记录子集合
     */
    public List<BaseDictOperation> selectBaseDictOperationList(BaseDictOperation baseDictOperation);
    /**
     * æ–°å¢žæ‚£è€…检查检验记录子
     *
     * @param baseDictOperation æ‚£è€…检查检验记录子
     * @return ç»“æžœ
     */
    public int insertBaseDictOperation(BaseDictOperation baseDictOperation);
    /**
     * ä¿®æ”¹æ‚£è€…检查检验记录子
     *
     * @param baseDictOperation æ‚£è€…检查检验记录子
     * @return ç»“æžœ
     */
    public int updateBaseDictOperation(BaseDictOperation baseDictOperation);
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param id æ‚£è€…检查检验记录子主键
     * @return ç»“æžœ
     */
    public int deleteBaseDictOperationById(Long id);
    /**
     * æ‰¹é‡åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param ids éœ€è¦åˆ é™¤çš„æ•°æ®ä¸»é”®é›†åˆ
     * @return ç»“æžœ
     */
    public int deleteBaseDictOperationByIds(Long[] ids);
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/mapper/PatMedOperationItemMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,119 @@
package com.ruoyi.othervisit.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.ruoyi.othervisit.domain.PatMedOperationItem;
/**
 * æ‚£è€…检查检验记录子Mapper接口
 *
 * @author lihu
 * @date 2025-02-25
 */
@Mapper
public interface PatMedOperationItemMapper
{
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子
     *
     * @param id æ‚£è€…检查检验记录子主键
     * @return æ‚£è€…检查检验记录子
     */
    public PatMedOperationItem selectPatMedOperationItemById(Long id);
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子列表
     *
     * @param patMedOperationItem æ‚£è€…检查检验记录子
     * @return æ‚£è€…检查检验记录子集合
     */
    public List<PatMedOperationItem> selectPatMedOperationItemList(PatMedOperationItem patMedOperationItem);
    /**
     * æ–°å¢žæ‚£è€…检查检验记录子
     *
     * @param patMedOperationItem æ‚£è€…检查检验记录子
     * @return ç»“æžœ
     */
    public int insertPatMedOperationItem(PatMedOperationItem patMedOperationItem);
    /**
     * æ–°å¢žæ‚£è€…检查检验记录子
     *
     * @param patMedOperationItems æ‚£è€…检查检验记录子
     * @return ç»“æžœ
     */
    public int insertPatMedOperationItems(List<PatMedOperationItem> patMedOperationItems);
    /**
     * ä¿®æ”¹æ‚£è€…检查检验记录子
     *
     * @param patMedOperationItem æ‚£è€…检查检验记录子
     * @return ç»“æžœ
     */
    public int updatePatMedOperationItem(PatMedOperationItem patMedOperationItem);
    /**
     * ä¿®æ”¹æ‚£è€…检查检验记录子
     *
     * @param patMedOperationItem æ‚£è€…检查检验记录子
     * @return ç»“æžœ
     */
    public int updatePatMedOperationItemByOperationid(PatMedOperationItem patMedOperationItem);
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param id æ‚£è€…检查检验记录子主键
     * @return ç»“æžœ
     */
    public int deletePatMedOperationItemById(Long id);
    /**
     * æ‰¹é‡åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param ids éœ€è¦åˆ é™¤çš„æ•°æ®ä¸»é”®é›†åˆ
     * @return ç»“æžœ
     */
    public int deletePatMedOperationItemByIds(Long[] ids);
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param opid æ‚£è€…检查检验记录子主键
     * @return ç»“æžœ
     */
    public int deleteHardPatMedOperationItemByOpId(String opid);
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param operationid æ‚£è€…检查检验记录子主键
     * @return ç»“æžœ
     */
    public int deletePatMedOperationItemByOperationid(Long operationid);
    /**
     * åˆ é™¤æ‚£è€…检查检验记录子
     *
     * @param opid æ‚£è€…检查检验记录子主键
     * @return ç»“æžœ
     */
    public int deleteSoftPatMedOperationItemByOpId(String opid);
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子列表
     *
     * @param patMedOperationItem æ‚£è€…检查检验记录子
     * @return æ‚£è€…检查检验记录子集合
     */
    public List<PatMedOperationItem> selectOperationItemList(PatMedOperationItem patMedOperationItem);
    /**
     * æŸ¥è¯¢æ‚£è€…检查检验记录子列表(同一opid下只取oplevelcode最大的记录)
     *
     * @param patMedOperationItem æ‚£è€…检查检验记录子
     * @return æ‚£è€…检查检验记录子集合
     */
    public List<PatMedOperationItem> selectOperationItemListMaxLevel(PatMedOperationItem patMedOperationItem);
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/mapper/PatMedOperationMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package com.ruoyi.othervisit.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import com.ruoyi.othervisit.domain.PatMedOperation;
/**
 * æ‚£è€…手术记录Mapper接口
 *
 * @author lihu
 * @date 2025-02-25
 */
@Mapper
public interface PatMedOperationMapper
{
    /**
     * æŸ¥è¯¢æ‚£è€…手术记录
     *
     * @param id æ‚£è€…手术记录主键
     * @return æ‚£è€…手术记录
     */
    public PatMedOperation selectPatMedOperationById(Long id);
    /**
     * æŸ¥è¯¢æ‚£è€…手术记录列表
     *
     * @param patMedOperation æ‚£è€…手术记录
     * @return æ‚£è€…手术记录集合
     */
    public List<PatMedOperation> selectPatMedOperationList(PatMedOperation patMedOperation);
    /**
     * æ–°å¢žæ‚£è€…手术记录
     *
     * @param patMedOperation æ‚£è€…手术记录
     * @return ç»“æžœ
     */
    public int insertPatMedOperation(PatMedOperation patMedOperation);
    /**
     * ä¿®æ”¹æ‚£è€…手术记录
     *
     * @param patMedOperation æ‚£è€…手术记录
     * @return ç»“æžœ
     */
    public int updatePatMedOperation(PatMedOperation patMedOperation);
    /**
     * åˆ é™¤æ‚£è€…手术记录
     *
     * @param id æ‚£è€…手术记录主键
     * @return ç»“æžœ
     */
    public int deletePatMedOperationById(Long id);
    /**
     * æ‰¹é‡åˆ é™¤æ‚£è€…手术记录
     *
     * @param ids éœ€è¦åˆ é™¤çš„æ•°æ®ä¸»é”®é›†åˆ
     * @return ç»“æžœ
     */
    public int deletePatMedOperationByIds(Long[] ids);
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/BaseDictOperationService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,87 @@
package com.ruoyi.othervisit.service;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.othervisit.domain.BaseDictOperation;
import com.ruoyi.othervisit.mapper.BaseDictOperationMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * æ‰‹æœ¯åŸºç¡€ä¿¡æ¯ï¼ˆ{@code base_dict_operation})基础读写。
 *
 * <h3>迁移对应(@ b25c3b51)</h3>
 * æ•´ä½“照搬 {@code com.smartor.service.impl.BaseDictOperationServiceImpl}:
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th></tr>
 *   <tr><td>{@link #selectById(Long)}</td><td>L32-34</td></tr>
 *   <tr><td>{@link #selectList(BaseDictOperation)}</td><td>L44-46</td></tr>
 *   <tr><td>{@link #insert(BaseDictOperation)}</td><td>L57-59(含 {@code setCreateTime})</td></tr>
 *   <tr><td>{@link #update(BaseDictOperation)}</td><td>L70-72(含 {@code setUpdateTime})</td></tr>
 *   <tr><td>{@link #deleteByIds(Long[])}</td><td>L82-84</td></tr>
 *   <tr><td>{@link #deleteById(Long)}</td><td>L94-96</td></tr>
 * </table>
 *
 * <h3>⚠️ æ—¶é—´è®¾ç½®ä¸ŽåŽŸå®žçŽ°ä¸åŒï¼ˆæœ‰æ„ï¼Œå·²ç™»è®°ï¼‰</h3>
 * åŽŸ {@code BaseDictOperationServiceImpl} çš„ insert/update <b>没有</b>设置
 * {@code createTime} / {@code updateTime}(与同批生成的 {@code PatMedOperationServiceImpl}
 * ä¸ä¸€æ · â€”— åŽè€…设了)。这里<b>照原样不设</b>,保持落库结果逐字一致。
 * è‹¥å°†æ¥è¦è¡¥ï¼Œå¿…须当成一次独立的行为变更,而不是"顺手对齐"。
 */
@Slf4j
@Service
public class BaseDictOperationService {
    @Autowired
    private BaseDictOperationMapper baseDictOperationMapper;
    /**
     * æŸ¥è¯¢æ‰‹æœ¯åŸºç¡€ä¿¡æ¯ã€‚
     * <p>原 {@code BaseDictOperationServiceImpl#selectBaseDictOperationById} L32-34。
     */
    public BaseDictOperation selectById(Long id) {
        return baseDictOperationMapper.selectBaseDictOperationById(id);
    }
    /**
     * æŸ¥è¯¢æ‰‹æœ¯åŸºç¡€ä¿¡æ¯åˆ—表。
     * <p>原 L44-46,一行转发。
     */
    public List<BaseDictOperation> selectList(BaseDictOperation baseDictOperation) {
        return baseDictOperationMapper.selectBaseDictOperationList(baseDictOperation);
    }
    /**
     * æ–°å¢žæ‰‹æœ¯åŸºç¡€ä¿¡æ¯ã€‚
     * <p>原 L57-59 â€”— âš ï¸ åŽŸå®žçŽ°<b>不设</b> {@code createTime},见类注释。
     */
    public int insert(BaseDictOperation baseDictOperation) {
        return baseDictOperationMapper.insertBaseDictOperation(baseDictOperation);
    }
    /**
     * ä¿®æ”¹æ‰‹æœ¯åŸºç¡€ä¿¡æ¯ã€‚
     * <p>原 L70-72 â€”— âš ï¸ åŽŸå®žçŽ°<b>不设</b> {@code updateTime},见类注释。
     */
    public int update(BaseDictOperation baseDictOperation) {
        return baseDictOperationMapper.updateBaseDictOperation(baseDictOperation);
    }
    /**
     * æ‰¹é‡åˆ é™¤æ‰‹æœ¯åŸºç¡€ä¿¡æ¯ã€‚
     * <p>原 L82-84,一行转发。
     */
    public int deleteByIds(Long[] ids) {
        return baseDictOperationMapper.deleteBaseDictOperationByIds(ids);
    }
    /**
     * åˆ é™¤æ‰‹æœ¯åŸºç¡€ä¿¡æ¯ã€‚
     * <p>原 L94-96,一行转发。
     */
    public int deleteById(Long id) {
        return baseDictOperationMapper.deleteBaseDictOperationById(id);
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationItemService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,150 @@
package com.ruoyi.othervisit.service;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.othervisit.domain.PatMedOperationItem;
import com.ruoyi.othervisit.mapper.PatMedOperationItemMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * æ‚£è€…手术明细({@code pat_med_operation_item})基础读写 â€”— æœ¬æ¨¡å—<b>唯一</b>的写入口。
 *
 * <h3>迁移对应(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th></tr>
 *   <tr><td>{@link #selectById(Long)}</td><td>{@code PatMedOperationItemServiceImpl} L32-34</td></tr>
 *   <tr><td>{@link #selectList(PatMedOperationItem)}</td><td>同文件 L44-46</td></tr>
 *   <tr><td>{@link #insert(PatMedOperationItem)}</td><td>同文件 L57-59(含 {@code setCreateTime})</td></tr>
 *   <tr><td>{@link #update(PatMedOperationItem)}</td><td>同文件 L70-72(含 {@code setUpdateTime})</td></tr>
 *   <tr><td>{@link #deleteByIds(Long[])} / {@link #deleteById(Long)}</td><td>同文件 L82-84 / L94-96</td></tr>
 *   <tr><td>其余 7 ä¸ªæ–¹æ³•</td><td><b>原来就没有 Service</b>:调用方各自注入 Mapper ç›´è°ƒ
 *       ï¼ˆ{@code CollectHISServiceImpl} / {@code ServiceExternalServiceImpl} /
 *       {@code PatMedInhospServiceImpl} / {@code ServiceSubtaskServiceImpl}),
 *       æœ¬æ¬¡ä¸€å¹¶æ”¶è¿›æœ¬ç±»</td></tr>
 * </table>
 *
 * <h3>⚠️ ä¸Ž {@code pat_med_inhosp} çš„联查(跨模块读,已登记白名单)</h3>
 * {@link #selectOperationItemListMaxLevel(PatMedOperationItem)} ä¸Ž
 * {@link #selectOperationItemList(PatMedOperationItem)} çš„ XML é‡Œ {@code JOIN pat_med_inhosp}
 * å– {@code endtime} / {@code inhospstate} / {@code serialnum}。
 * è¿™ä¸¤æ¡ SQL æ˜¯<b>手术域自己的查询</b>(出入院表在这里只作为维度来源),
 * æ•…随实体一起搬进本模块,而不是改成跨模块 API è°ƒç”¨ â€”—
 * è§ {@code doc/跨模块数据访问原则与收口方案.md} çš„联查白名单。
 *
 * <h3>命名说明</h3>
 * æœ¬ç±»åˆ»æ„<b>不</b>叫 {@code PatMedOperationItemService},与 Controller
 * {@code OperationItemController} å¯¹é½ï¼Œé¿å…ä¸Ž smartor ä¾§å·²åˆ é™¤çš„
 * {@code IPatMedOperationItemService} æ··æ·†ã€‚
 */
@Slf4j
@Service
public class OperationItemService {
    @Autowired
    private PatMedOperationItemMapper patMedOperationItemMapper;
    // ==========================================================================
    // æŸ¥
    // ==========================================================================
    public PatMedOperationItem selectById(Long id) {
        return patMedOperationItemMapper.selectPatMedOperationItemById(id);
    }
    public List<PatMedOperationItem> selectList(PatMedOperationItem patMedOperationItem) {
        return patMedOperationItemMapper.selectPatMedOperationItemList(patMedOperationItem);
    }
    /**
     * æŒ‰æ¡ä»¶æŸ¥æ‰‹æœ¯æ˜Žç»†ï¼ˆåŽŸè°ƒç”¨æ–¹ï¼š{@code CollectHISServiceImpl}、{@code ServiceExternalServiceImpl})。
     * <p>
     * âš ï¸ ä¸Ž {@link #selectList(PatMedOperationItem)} <b>不是同一条 SQL</b>
     * ï¼ˆ{@code selectOperationItemList} vs {@code selectPatMedOperationItemList}),
     * ä¸”前者 {@code JOIN pat_med_inhosp}。别互相替代。
     */
    public List<PatMedOperationItem> selectOperationItemList(PatMedOperationItem patMedOperationItem) {
        return patMedOperationItemMapper.selectOperationItemList(patMedOperationItem);
    }
    /**
     * æŒ‰æ¡ä»¶æŸ¥æ‰‹æœ¯æ˜Žç»†ï¼ŒåŒä¸€ {@code opid} ä¸‹åªå– {@code oplevelcode} æœ€å¤§çš„一条。
     * <p>
     * åŽŸè°ƒç”¨æ–¹ï¼š{@code PatMedInhospServiceImpl}(出入院随访的手术分支,L457 / L1259)。
     * åŒæ—¶ {@code JOIN pat_med_inhosp} â€”— è§ç±»æ³¨é‡Šçš„跨模块读白名单说明。
     */
    public List<PatMedOperationItem> selectOperationItemListMaxLevel(PatMedOperationItem patMedOperationItem) {
        return patMedOperationItemMapper.selectOperationItemListMaxLevel(patMedOperationItem);
    }
    // ==========================================================================
    // å†™
    // ==========================================================================
    public int insert(PatMedOperationItem patMedOperationItem) {
        patMedOperationItem.setCreateTime(DateUtils.getNowDate());
        return patMedOperationItemMapper.insertPatMedOperationItem(patMedOperationItem);
    }
    /**
     * æ‰¹é‡æ–°å¢žï¼ˆåŽŸ Mapper å·²æœ‰ {@code insertPatMedOperationItems},
     * ä½†åŽŸ Service æœªæš´éœ²ã€è°ƒç”¨æ–¹é‚£å¤„是被注释掉的)。此处照实暴露,不改 SQL。
     */
    public int insertBatch(List<PatMedOperationItem> patMedOperationItems) {
        return patMedOperationItemMapper.insertPatMedOperationItems(patMedOperationItems);
    }
    public int update(PatMedOperationItem patMedOperationItem) {
        patMedOperationItem.setUpdateTime(DateUtils.getNowDate());
        return patMedOperationItemMapper.updatePatMedOperationItem(patMedOperationItem);
    }
    /**
     * æŒ‰ {@code operationid} æ›´æ–°ã€‚
     * <p>
     * âš ï¸ åŽŸ Service é‡Œ<b>没有</b>这个方法,调用方({@code CollectHISServiceImpl} L511)
     * æ˜¯ç›´è°ƒ Mapper çš„。注意它<b>不会</b>自动设 {@code updateTime}
     * ï¼ˆåŽŸè°ƒç”¨æ–¹è‡ªå·±è´Ÿè´£ï¼‰ï¼Œæ­¤å¤„ä¹Ÿ<b>不</b>代设 â€”— å…å¾—改变落库内容。
     */
    public int updateByOperationid(PatMedOperationItem patMedOperationItem) {
        return patMedOperationItemMapper.updatePatMedOperationItemByOperationid(patMedOperationItem);
    }
    public int deleteByIds(Long[] ids) {
        return patMedOperationItemMapper.deletePatMedOperationItemByIds(ids);
    }
    public int deleteById(Long id) {
        return patMedOperationItemMapper.deletePatMedOperationItemById(id);
    }
    /**
     * æŒ‰ {@code opid} ç‰©ç†åˆ é™¤ã€‚
     * <p>
     * âš ï¸ <b>名字里带 Hard,确实是物理删除</b>(对比 {@link #deleteSoftByOpId(String)})。
     * è°ƒç”¨æ–¹ï¼š{@code ServiceExternalServiceImpl} L1534。
     *
     * @param opid ä¸»æ‰‹æœ¯ID(原调用方传的是 {@code String.valueOf(id)})
     */
    public int deleteHardByOpId(String opid) {
        return patMedOperationItemMapper.deleteHardPatMedOperationItemByOpId(opid);
    }
    /**
     * æŒ‰ {@code opid} è½¯åˆ é™¤ã€‚
     * <p>调用方:{@code CollectHISServiceImpl} L444、{@code ServiceExternalServiceImpl} L1645。
     */
    public int deleteSoftByOpId(String opid) {
        return patMedOperationItemMapper.deleteSoftPatMedOperationItemByOpId(opid);
    }
    /**
     * æŒ‰ {@code operationid} åˆ é™¤ã€‚
     * <p>调用方:{@code CollectHISServiceImpl} L502。
     */
    public int deleteByOperationid(Long operationid) {
        return patMedOperationItemMapper.deletePatMedOperationItemByOperationid(operationid);
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
package com.ruoyi.othervisit.service;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.othervisit.domain.PatMedOperation;
import com.ruoyi.othervisit.mapper.PatMedOperationMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
/**
 * æ‚£è€…手术记录({@code pat_med_operation})基础读写 â€”— æœ¬æ¨¡å—<b>唯一</b>的写入口。
 *
 * <h3>迁移对应(@ b25c3b51)</h3>
 * æ•´ä½“照搬 {@code com.smartor.service.impl.PatMedOperationServiceImpl} é‡Œçš„ 6 ä¸ªçº¯ CRUD æ–¹æ³•:
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th></tr>
 *   <tr><td>{@link #selectById(Long)}</td><td>L46-48</td></tr>
 *   <tr><td>{@link #selectList(PatMedOperation)}</td><td>L57-59</td></tr>
 *   <tr><td>{@link #insert(PatMedOperation)}</td><td>L68-71(含 {@code setCreateTime})</td></tr>
 *   <tr><td>{@link #update(PatMedOperation)}</td><td>L80-83(含 {@code setUpdateTime})</td></tr>
 *   <tr><td>{@link #deleteByIds(Long[])}</td><td>L92-94</td></tr>
 *   <tr><td>{@link #deleteById(Long)}</td><td>L103-105</td></tr>
 * </table>
 *
 * <h3>⚠️ ä¸ŽåŽŸå®žçŽ°é€å­—ä¸€è‡´çš„åœ°æ–¹ï¼ˆå‹¿"顺手优化")</h3>
 * <ul>
 *   <li>{@code insert} ç”¨ {@code DateUtils.getNowDate()} è®¾ {@code createTime},
 *       {@code update} ç”¨åŒä¸€ä¸ªæ–¹æ³•设 {@code updateTime} â€”— åŽŸæ ·ä¿ç•™ï¼Œ
 *       æ¢æˆ {@code new Date()} ä¼šè®©"当天"的判定语义变化({@code getNowDate} æœ‰æ—¥æœŸå½’一逻辑)。</li>
 * </ul>
 *
 * <h3>为什么必须有这一层</h3>
 * æŒ‰è·¨æ¨¡å—数据访问原则:{@code pat_med_operation} çš„<b>写操作一律不许跨模块</b>。
 * å…¶å®ƒæ¨¡å—(HIS é‡‡é›† {@code CollectHISServiceImpl}、外部手术回传 {@code ServiceExternalServiceImpl}、
 * å‡ºå…¥é™¢éšè®¿ {@code PatMedInhospServiceImpl})需要读写手术数据时,
 * <b>只能</b>经 {@link com.ruoyi.othervisit.api.OtherVisitFacade} è°ƒç”¨æœ¬ç±»ï¼Œ
 * ä¸å¾—再注入 {@link PatMedOperationMapper}。
 */
@Slf4j
@Service
public class OperationService {
    @Autowired
    private PatMedOperationMapper patMedOperationMapper;
    /**
     * æŸ¥è¯¢æ‚£è€…手术记录。
     * <p>原 {@code PatMedOperationServiceImpl#selectPatMedOperationById} L46-48,一行转发。
     */
    public PatMedOperation selectById(Long id) {
        return patMedOperationMapper.selectPatMedOperationById(id);
    }
    /**
     * æŸ¥è¯¢æ‚£è€…手术记录列表。
     * <p>
     * åŽŸ {@code PatMedOperationServiceImpl#selectPatMedOperationList} L57-59,一行转发。
     * <p>
     * âš ï¸ ä¸Žå‡ºå…¥é™¢/门诊不同,这里<b>没有</b>存储过程也没有展示富化 â€”—
     * æ‰‹æœ¯åˆ—表查询一直是普通 {@code selectPatMedOperationList},原样保留。
     */
    public List<PatMedOperation> selectList(PatMedOperation patMedOperation) {
        return patMedOperationMapper.selectPatMedOperationList(patMedOperation);
    }
    /**
     * æ–°å¢žæ‚£è€…手术记录。
     * <p>原 L68-71:先 {@code setCreateTime(DateUtils.getNowDate())} å† insert。
     */
    public int insert(PatMedOperation patMedOperation) {
        patMedOperation.setCreateTime(DateUtils.getNowDate());
        return patMedOperationMapper.insertPatMedOperation(patMedOperation);
    }
    /**
     * ä¿®æ”¹æ‚£è€…手术记录。
     * <p>原 L80-83:先 {@code setUpdateTime(DateUtils.getNowDate())} å† update。
     */
    public int update(PatMedOperation patMedOperation) {
        patMedOperation.setUpdateTime(DateUtils.getNowDate());
        return patMedOperationMapper.updatePatMedOperation(patMedOperation);
    }
    /**
     * æ‰¹é‡åˆ é™¤æ‚£è€…手术记录。
     * <p>
     * åŽŸ L92-94,一行转发。
     * <p>
     * âš ï¸ åº•层 XML é‡Œ {@code deletePatMedOperationById} / {@code deletePatMedOperationByIds}
     * éƒ½æ˜¯ {@code <update>}(软删,置 {@code del_flag}),不是物理删除 â€”— åå­—有误导性,原样保留。
     */
    public int deleteByIds(Long[] ids) {
        return patMedOperationMapper.deletePatMedOperationByIds(ids);
    }
    /**
     * åˆ é™¤æ‚£è€…手术记录。
     * <p>原 L103-105,一行转发(同上,底层是软删)。
     */
    public int deleteById(Long id) {
        return patMedOperationMapper.deletePatMedOperationById(id);
    }
}
ruoyi-othervisit/src/main/java/com/ruoyi/othervisit/service/OperationVisitTaskService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,367 @@
package com.ruoyi.othervisit.service;
import com.ruoyi.common.core.service.ArchiveReadApi;
import com.ruoyi.common.core.service.ServiceTaskMatchApi;
import com.ruoyi.common.core.service.VisitTaskCreateApi;
import com.ruoyi.common.core.service.dto.ServiceTaskMatchDto;
import com.ruoyi.common.core.service.dto.VisitArchiveRow;
import com.ruoyi.common.core.service.dto.VisitLongTaskDto;
import com.ruoyi.common.core.service.dto.VisitSubtaskDraft;
import com.ruoyi.othervisit.domain.PatMedOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
 * æ‰‹æœ¯éšè®¿ç¼–排:扫描未处理的手术记录 â†’ æŒ‰<b>手术编码</b>匹配长期任务 â†’ å†™å­ä»»åŠ¡ã€‚
 *
 * <h3>迁移对应(@ b25c3b51)</h3>
 * <table border="1">
 *   <tr><th>本类方法</th><th>原实现</th></tr>
 *   <tr><td>{@link #dealOperationInfo()}</td>
 *       <td>{@code PatMedOperationServiceImpl#dealOperationInfo} L107-149(42 è¡Œï¼‰</td></tr>
 *   <tr><td>{@link #writeInSubTask}</td>
 *       <td>同文件 L152-187(36 è¡Œï¼‰</td></tr>
 *   <tr><td>{@link #boxedServiceSubtask}</td>
 *       <td>同文件 L191-238(48 è¡Œï¼Œ29 ä¸ª setter)</td></tr>
 *   <tr><td>{@link #addDays}</td><td>同文件 L240-248</td></tr>
 * </table>
 *
 * <h3>跨模块能力怎么拿(本模块只依赖 ruoyi-common)</h3>
 * <ul>
 *   <li>{@link ServiceTaskMatchApi#findLongTasksByOpcode(String)} â€”—
 *       æŒ‰æ‰‹æœ¯ç¼–码匹配长期任务(原 {@code serviceTaskoperMapper.selectServiceTaskoperList},
 *       {@code longtask=1} + {@code opcode=?});</li>
 *   <li>{@link VisitTaskCreateApi#findPublishedLongTask(Long)} â€”— æŸ¥å·²å‘布长期任务
 *       ï¼ˆåŽŸ {@code serviceTaskMapper.selectServiceTaskList} é… {@code sendState=2});</li>
 *   <li>{@link VisitTaskCreateApi#insertSubtask(VisitSubtaskDraft)} â€”— æŠŠç»„装好的草稿落库
 *       ï¼ˆåŽŸ {@code serviceSubtaskMapper.insertServiceSubtask});</li>
 *   <li>{@link ArchiveReadApi#findByPatid(Long)} â€”— æ‚£è€…档案只读
 *       ï¼ˆåŽŸ {@code patArchiveMapper.selectPatArchiveByPatid})。</li>
 * </ul>
 * ä»»åŠ¡åˆ›å»ºæœ¬èº«<b>本次不迁</b>:你要求它将来单独成一个模块,本模块只调上面这些接口,
 * å°†æ¥æ¢å®žçŽ°æ–¹æ—¶æœ¬ç±»<b>一行都不用改</b>。
 *
 * <h3>⚠️ ä¿ç•™çš„原实现行为(勿"顺手优化")</h3>
 * <ol>
 *   <li>{@code dealOperationInfo} è¿”回<b>固定 {@code 1}</b>(不是处理条数);</li>
 *   <li>匹配不到任务 â†’ {@code checkFlag="2"} + åŽŸå› é€å­—ç…§æŠ„
 *       ã€Œè¯¥æ‰‹æœ¯ç¼–码,未配置长期任务;」(<b>注意末尾是分号,且与出入院/门诊的文案不同</b>);</li>
 *   <li>匹配到<b>多个</b>任务 â†’ åŽŸå› å†™ã€Œè¯¥æ‰‹æœ¯ç¼–ç ï¼Œæœ‰å¤šä¸ªéšè®¿é•¿æœŸä»»åŠ¡:」+ <b>taskIds åˆ—表的 toString</b>,
 *       å…ˆ update ä¸€æ¬¡ï¼Œç„¶åŽ<b>只用第一个</b>继续;</li>
 *   <li>⚠️ <b>记录本身不参与"多任务"分支的 checkFlag èµ‹å€¼</b>:那一支只写 {@code longTaskReason},
 *       ä¸å†™ {@code checkFlag},随后由 {@code writeInSubTask} æˆåŠŸæ—¶ç½® {@code "1"} â€”— åŽŸæ ·ä¿ç•™ï¼›</li>
 *   <li>⚠️ æ¯æ¡è®°å½•的整段处理被 {@code try/catch(Exception)} åŒ…住,异常只
 *       {@code printStackTrace()} + {@code log.error} åŽ<b>继续下一条</b>
 *       ï¼ˆåŽŸå®žçŽ°ä¼šåžæŽ‰å¼‚å¸¸ï¼‰ï¼›æœ¬å®žçŽ°æŠŠå…¥å‚ä»Ž {@code PatArchive} æ¢æˆäº† {@link VisitArchiveRow},
 *       ä½† {@code getNotrequiredFlag()} åœ¨ {@link VisitArchiveRow} é‡Œä»æ˜¯ {@code String},
 *       æ‰€ä»¥"档案查不到 â†’ NPE â†’ è¢«åž â†’ è¯¥æ¡ä¸å¤„理"这一行为逐字保持;</li>
 *   <li>{@code check=true} æ˜¯<b>硬编码</b>传入的({@link #dealOperationInfo()} é‡Œå†™æ­»ï¼‰ï¼Œ
 *       è€Œ {@code writeInSubTask} çš„ {@code check} åˆ†æ”¯å®žé™…上没有任何判断
 *       â€”— è¿™æ˜¯åŽŸå®žçŽ°çš„å†™æ³•ï¼Œä¸è¦ä¸ºå®ƒè¡¥é€»è¾‘ï¼›</li>
 *   <li>{@code setHospType("2")} æ˜¯<b>硬编码</b>,不读 {@code serviceTask.getHospType()}。</li>
 * </ol>
 *
 * <h3>⚠️ ä¸Žé—¨è¯Šç‰ˆçš„三处不同(别照抄门诊)</h3>
 * <ol>
 *   <li><b>没有"先停旧再新增"</b>:{@code check} å‚数被接收但完全不使用,
 *       æ‰‹æœ¯è·¯å¾„从不查/停待执行子任务;</li>
 *   <li><b>不使用 {@code OuthospSubtaskDraft}</b>:手术的 {@code admitdate}/{@code fudate}/
 *       {@code finishtime} ä¸‰ä¸ªå­—段在原实现里就没有被 set,故直接用 {@link VisitSubtaskDraft};</li>
 *   <li>入参是<b>整个 {@link PatMedOperation}</b>(不是明细),
 *       å› ä¸ºåŽŸå®žçŽ°å¯¹ {@code pat_med_operation} é€æ¡å¤„理。</li>
 * </ol>
 *
 * <h3>开关</h3>
 * æœ¬ç±»ä¸å«å¼€å…³åˆ¤æ–­ â€”— ç”± {@link com.ruoyi.othervisit.api.OtherVisitFacade} ç»Ÿä¸€å†³å®šæ˜¯å¦è°ƒç”¨ã€‚
 */
@Slf4j
@Service
public class OperationVisitTaskService {
    /** å¾…执行状态(原实现硬编码 2L) */
    private static final Long SENDSTATE_PENDING = 2L;
    /** ä¸æ‰§è¡ŒçŠ¶æ€ï¼ˆåŽŸå®žçŽ°ç¡¬ç¼–ç  4L:"患者不需要随访"时用) */
    private static final Long SENDSTATE_SKIP = 4L;
    /** æ ‡è®°"不需要随访"的值(原 {@code patArchive.notrequiredFlag == "1"}) */
    private static final String NOT_REQUIRED_YES = "1";
    /** æ‰‹æœ¯ä¾§ {@code hospType} çš„固定值(原实现硬编码) */
    private static final String HOSP_TYPE_OPERATION = "2";
    @Autowired
    private OperationService operationService;
    @Autowired
    private ObjectProvider<ArchiveReadApi> archiveApiProvider;
    @Autowired
    private ObjectProvider<ServiceTaskMatchApi> matchApiProvider;
    @Autowired
    private ObjectProvider<VisitTaskCreateApi> taskApiProvider;
    /**
     * æ‰‹æœ¯éšè®¿ä¸»æ‰«æã€‚
     * <p>
     * é€è¡Œå¯¹åº”原 {@code dealOperationInfo}(L107-149):
     * <pre>
     * PatMedOperation q = new PatMedOperation();
     * q.setCheckFlag("0");                       // æœªå¤„理
     * List&lt;PatMedOperation&gt; list = selectPatMedOperationList(q);
     * log.error("dealOperationInfo需要处理的手术信息:{}", list);   // âš ï¸ åŽŸå®žçŽ°æ˜¯ error çº§
     * for (每条记录) {
     *     æŸ¥æ¡£æ¡ˆï¼›
     *     try {
     *         æŒ‰ opcode æŸ¥ longtask=1 çš„任务;
     *         ä¸ºç©º  -> checkFlag="2" + "该手术编码,未配置长期任务;" + update + continue
     *         å¤šä¸ª  -> longTaskReason="该手术编码,有多个随访长期任务:"+taskIds + update,只留第一个
     *         writeInSubTask(第一个任务ID, true, è®°å½•, æ¡£æ¡ˆ);
     *     } catch (Exception e) { e.printStackTrace(); log.error("人员处理出异常了:{}", e.getMessage()); }
     * }
     * return 1;   // å›ºå®šå€¼
     * </pre>
     *
     * @return å›ºå®š {@code 1}(与原实现一致,<b>不是</b>处理条数)
     */
    public Integer dealOperationInfo() {
        ServiceTaskMatchApi matchApi = matchApi();
        VisitTaskCreateApi taskApi = taskApi();
        ArchiveReadApi archiveApi = archiveApi();
        if (matchApi == null || taskApi == null) {
            log.warn("【手术随访】跨模块契约不可用(ServiceTaskMatchApi={}, VisitTaskCreateApi={}),跳过本次手术随访。",
                    matchApi != null, taskApi != null);
            return 1;
        }
        PatMedOperation query = new PatMedOperation();
        // èŽ·å–éœ€è¦å‡ºé™¢éšè®¿ï¼Œæœªå¤„ç†çš„æ•°æ®
        query.setCheckFlag("0");
        List<PatMedOperation> patMedOperations = operationService.selectList(query);
        // âš ï¸ åŽŸå®žçŽ°ç”¨çš„æ˜¯ log.error(不是 info/warn),级别照抄
        log.error("dealOperationInfo需要处理的手术信息:{}", patMedOperations);
        if (CollectionUtils.isEmpty(patMedOperations)) {
            return 1;
        }
        for (PatMedOperation patMedOperation1 : patMedOperations) {
            // èŽ·å–æ‚£è€…ä¿¡æ¯
            VisitArchiveRow patArchive = archiveApi == null ? null : archiveApi.findByPatid(patMedOperation1.getPatid());
            try {
                // æ ¹æ®æ‚£è€…所在科室,获取该手术编码对应的长期任务
                List<ServiceTaskMatchDto> serviceTaskopers = matchApi.findLongTasksByOpcode(patMedOperation1.getOpcode());
                // å¦‚果部门模板为空(暂定,将出入院表的check_flag设置为2  ï¼Œlong_task_reason è®°å½•原因)
                if (CollectionUtils.isEmpty(serviceTaskopers) || serviceTaskopers.size() == 0) {
                    patMedOperation1.setCheckFlag("2");
                    patMedOperation1.setLongTaskReason("该手术编码,未配置长期任务;");
                    operationService.update(patMedOperation1);
                    continue;
                } else if (serviceTaskopers.size() > 1) {
                    // å¦‚果查询出多个任务,那就取第一个,并在患者备注一下
                    List<Long> taskIds = serviceTaskopers.stream().map(ServiceTaskMatchDto::getTaskId).collect(Collectors.toList());
                    patMedOperation1.setLongTaskReason("该手术编码,有多个随访长期任务:" + taskIds);
                    operationService.update(patMedOperation1);
                    ServiceTaskMatchDto serviceTaskoper1 = serviceTaskopers.get(0);
                    serviceTaskopers = new ArrayList<>();
                    serviceTaskopers.add(serviceTaskoper1);
                }
                writeInSubTask(taskApi, serviceTaskopers.get(0).getTaskId(), Boolean.TRUE, patMedOperation1, patArchive);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("人员处理出异常了:{}", e.getMessage());
            }
        }
        return 1;
    }
    /**
     * æŠŠä¸€æ¡æ‰‹æœ¯è®°å½•写入随访子任务。
     * <p>
     * é€è¡Œå¯¹åº”原 {@code PatMedOperationServiceImpl#writeInSubTask}(L152-187)。
     *
     * <h3>⚠️ {@code check} å‚数在原实现里<b>没有被使用</b></h3>
     * åŽŸæ–¹æ³•ç­¾åå¸¦ {@code Boolean check},但方法体里<b>一次都没读它</b>
     * ï¼ˆä¸åƒé—¨è¯Šç‰ˆä¼šç”¨å®ƒå†³å®š"先停旧再新增")。这里刻意保留同样的签名与"不读"的写法,
     * ä»¥å…å°†æ¥å¯¹ç…§ä»£ç æ—¶è¯¯ä»¥ä¸ºæ¼äº†ä¸€æ®µé€»è¾‘。
     *
     * @param check åŽŸå®žçŽ°ä¼ å…¥ä½†æœªä½¿ç”¨ï¼›ä¿ç•™ç­¾ååªä¸ºé€è¡Œå¯æ¯”
     */
    private void writeInSubTask(VisitTaskCreateApi taskApi, Long taskid, Boolean check,
                                PatMedOperation patMedOperation1, VisitArchiveRow patArchive) {
        String longTaskReason = patMedOperation1.getLongTaskReason();
        if (StringUtils.isEmpty(longTaskReason)) longTaskReason = "";
        // åŽŸ L156-159:查「已发布的长期任务」(taskid + sendState=2)
        VisitLongTaskDto serviceTask = taskApi.findPublishedLongTask(taskid);
        if (serviceTask == null) {
            log.error("该手术编码长期任务不存在,任务ID为:{}", taskid);
            patMedOperation1.setCheckFlag("2");
            patMedOperation1.setLongTaskReason(longTaskReason + " & è¯¥æ‰‹æœ¯ç¼–码长期任务不存在,任务ID为:" + taskid);
            operationService.update(patMedOperation1);
            return;
        }
        // å°è£…serviceSubtask
        VisitSubtaskDraft serviceSubtask = boxedServiceSubtask(serviceTask, patMedOperation1, patArchive);
        boolean i = taskApi.insertSubtask(serviceSubtask);
        if (i) {
            // å°†check_flag改成1(已处理)
            PatMedOperation patMedOperation = new PatMedOperation();
            patMedOperation.setId(patMedOperation1.getId());
            patMedOperation.setCheckFlag("1");
            operationService.update(patMedOperation);
        } else {
            // ç”Ÿæˆå­ä»»åŠ¡å¤±è´¥ï¼Œ
            log.error("生成子任务失败serviceSubtask为:{}", serviceSubtask);
            PatMedOperation patMedOperation = new PatMedOperation();
            patMedOperation.setId(patMedOperation1.getId());
            patMedOperation.setRemark("生成子任务失败");
            operationService.update(patMedOperation);
        }
    }
    /**
     * æ‰‹æœ¯ç‰ˆå­ä»»åŠ¡ç»„è£…ã€‚
     * <p>
     * é€å­—段对应原 {@code boxedServiceSubtask}(L191-238,29 ä¸ª setter)。
     *
     * <h3>原实现第一行是 {@code DtoConversionUtils.sourceToTarget(serviceTask, ServiceSubtask.class)}</h3>
     * å®ƒæ•´æ‰¹æ‹·äº† {@code ServiceTask} ä¸Ž {@code ServiceSubtask} çš„ 33 ä¸ªåŒåå­—段,
     * å…¶ä¸­ 18 ä¸ªä¹‹åŽå†æ²¡è¢«æ˜¾å¼è¦†ç›–。本次换了数据来源——
     * {@code serviceTask} çŽ°åœ¨æ˜¯ {@link VisitLongTaskDto}(15 ä¸ªå­—段的任务快照),
     * <b>不是</b> {@code ServiceTask} å®žä½“。
     * <p>
     * âš ï¸ <b>这是一个已登记的、与门诊版完全相同的口径限制</b>:
     * é‚£ 18 ä¸ª"靠拷贝带进子任务"的字段里,{@code VisitLongTaskDto} åªæ‰¿è½½
     * {@code taskName}/{@code type}/{@code preachformDesc}/{@code createBy} ç­‰å·²åˆ—出的几个,
     * å…¶ä½™ï¼ˆ{@code guid}/{@code pid}/{@code sendType}/{@code isupload}/…)
     * éœ€è¦ç”±å°†æ¥çš„「随访任务创建模块」在实现 {@code insertSubtask} æ—¶è¡¥é½ã€‚
     * æœ¬æ¨¡å—<b>不</b>自己补,也不把它们加到 {@code VisitSubtaskDraft} ä¸Š â€”—
     * é‚£ä¼šè®©å‡ºå…¥é™¢/门诊两条已验收的链路多写列。
     * è½åœ°æ–¹å¼ä¸Žé—¨è¯Šä¾§ï¼ˆ{@code OuthospVisitTaskService#boxedServiceSubtask})一致。
     *
     * <h3>⚠️ åˆ»æ„ä¿ç•™çš„原行为</h3>
     * <ul>
     *   <li>{@code setStarttime} ä¸Ž {@code setEndtime} <b>同值</b>(都取 {@code opreqtime}),原样保留;</li>
     *   <li>{@code serviceTask.getSendDay() == null} æ—¶æŒ‰ 1 å¤©å¤„理,且<b>写回 serviceTask</b>;</li>
     *   <li>{@code addDays} çš„基准是 {@code opreqtime}(可能为 null,
     *       åŽŸå®žçŽ°åŒæ ·ä¼š NPE â€”— ä¸é¢å¤–加保护);</li>
     *   <li>不需要随访时置 {@code sendstate=4} + {@code result="error"} å¹¶æŠŠåŽŸå› å†™è¿› remark,
     *       ä½†<b>仍然继续 insert</b>(原实现没有提前 return);</li>
     *   <li>{@code setPhone} åªåœ¨ {@code telcode} ä¸ºç©ºæ—¶æ‰å›žè½åˆ° {@code relativetelcode}
     *       ï¼ˆä¸Žé—¨è¯Šç‰ˆ"三元表达式"写法等价,此处照抄原 if å†™æ³•)。</li>
     * </ul>
     */
    public VisitSubtaskDraft boxedServiceSubtask(VisitLongTaskDto serviceTask,
                                                 PatMedOperation patMedOperation1,
                                                 VisitArchiveRow patArchive) {
        VisitSubtaskDraft serviceSubtask = new VisitSubtaskDraft();
        serviceSubtask.setTaskid(serviceTask.getTaskid());
        serviceSubtask.setOrgid(serviceTask.getOrgid());
        serviceSubtask.setCampusid(serviceTask.getCampusid());
        // åŽŸ L194-195:libtemplateid åœ¨ ServiceTask é‡Œæ˜¯ String,落库列/草稿里是 Long,
        // åŽŸå®žçŽ°æ˜¾å¼ Long.valueOf è½¬æ¢ï¼Œä¸”【仅在非空时】设值(为空则保持 null -> åˆ—不写)。原样保留。
        if (StringUtils.isNotEmpty(serviceTask.getLibtemplateid()))
            serviceSubtask.setLibtemplateid(Long.valueOf(serviceTask.getLibtemplateid()));
        serviceSubtask.setNurseId(patMedOperation1.getNurseId());
        serviceSubtask.setNurseName(patMedOperation1.getNurseName());
//        serviceSubtask.setInhospid(patMedOperation1.getInhospid());
        serviceSubtask.setDrcode(patMedOperation1.getDrcode());
        serviceSubtask.setDrname(patMedOperation1.getDrname());
        serviceSubtask.setDeptcode(patMedOperation1.getDeptcode());
        serviceSubtask.setDeptname(patMedOperation1.getDeptname());
        serviceSubtask.setTemplateid(serviceTask.getTemplateid());
        serviceSubtask.setTemplatename(serviceTask.getTemplatename());
        serviceSubtask.setPatid(patArchive.getId());
        serviceSubtask.setSendname(patArchive.getName());
        serviceSubtask.setSfzh(patArchive.getIdcardno());
        serviceSubtask.setPhone(patArchive.getTelcode());
        if (StringUtils.isBlank(patArchive.getTelcode())) serviceSubtask.setPhone(patArchive.getRelativetelcode());
        serviceSubtask.setSex(patArchive.getSex());
        serviceSubtask.setAge(patArchive.getAge());
        serviceSubtask.setSendstate(SENDSTATE_PENDING);
        serviceSubtask.setStarttime(patMedOperation1.getOpreqtime());
        serviceSubtask.setEndtime(patMedOperation1.getOpreqtime());
        serviceSubtask.setServiceType("2");
        serviceSubtask.setPreachform(serviceTask.getPreachform());
        serviceSubtask.setHospType(HOSP_TYPE_OPERATION);
        serviceSubtask.setCreateTime(new Date());
        serviceSubtask.setUpdateTime(new Date());
//        serviceSubtask.setCreateBy(patMedInhosp1.getNurseName());
//        serviceSubtask.setLeavehospitaldistrictcode(patMedInhosp1.getLeavehospitaldistrictcode());
//        serviceSubtask.setLeavehospitaldistrictname(patMedInhosp1.getLeavehospitaldistrictname());
        // è®¾ç½®å‘送时间
        if (serviceTask.getSendDay() == null) serviceTask.setSendDay(1L);
        Date newDate = addDays(patMedOperation1.getOpreqtime(), serviceTask.getSendDay().intValue());
        serviceSubtask.setLongSendTime(newDate);
        serviceSubtask.setVisitTime(newDate);
        // æ‚£è€…发送时间
        if (StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) {
            String remark = patArchive.getNotrequiredreason();
            serviceSubtask.setRemark(remark);
            serviceSubtask.setResult("error");
//            serviceSubtask.setFinishtime(new Date());
            // ä¸æ‰§è¡Œ
            serviceSubtask.setSendstate(SENDSTATE_SKIP);
        }
        return serviceSubtask;
    }
    /**
     * åœ¨ {@code date} ä¸ŠåŠ  {@code days} å¤©ï¼›{@code days} ä¸º null æ—¶æŒ‰ 1 å¤©ã€‚
     * <p>
     * é€è¡Œå¯¹åº”原 {@code PatMedOperationServiceImpl#addDays}(L240-248)。
     * âš ï¸ ä¸Žå‡ºå…¥é™¢ä¾§çš„ {@code minusDay} ä¸æ˜¯åŒä¸€ä¸ªæ–¹æ³•(那个硬编码减 2 å¤©å¹¶å¿½ç•¥å…¥å‚)。
     */
    private Date addDays(Date date, Integer days) {
        if (days == null) {
            days = 1;
        }
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        calendar.add(Calendar.DAY_OF_MONTH, days);
        return calendar.getTime();
    }
    // ==========================================================================
    // å¥‘约取用(与门诊/出入院模块同一写法:ObjectProvider + stream,取不到就降级)
    // ==========================================================================
    private ServiceTaskMatchApi matchApi() {
        return providerGet(matchApiProvider, "ServiceTaskMatchApi");
    }
    private ArchiveReadApi archiveApi() {
        return providerGet(archiveApiProvider, "ArchiveReadApi");
    }
    private VisitTaskCreateApi taskApi() {
        return providerGet(taskApiProvider, "VisitTaskCreateApi");
    }
    private <T> T providerGet(ObjectProvider<T> p, String name) {
        if (p == null) {
            return null;
        }
        try {
            return p.stream().findFirst().orElse(null);
        } catch (Exception e) {
            log.warn("【手术随访】获取 {} å¤±è´¥ï¼ŒæŒ‰ä¸å¯ç”¨å¤„理:{}", name, e.toString());
            return null;
        }
    }
}
ruoyi-othervisit/src/main/resources/mapper/othervisit/BaseDictOperationMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,280 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.othervisit.mapper.BaseDictOperationMapper">
    <resultMap type="com.ruoyi.othervisit.domain.BaseDictOperation" id="BaseDictOperationResult">
        <result property="id" column="id"/>
        <result property="opid" column="opid"/>
        <result property="opcode" column="opcode"/>
        <result property="opdesc" column="opdesc"/>
        <result property="incitypecode" column="incitypecode"/>
        <result property="incitypedesc" column="incitypedesc"/>
        <result property="oplevelcode" column="oplevelcode"/>
        <result property="opleveldesc" column="opleveldesc"/>
        <result property="opposition" column="opposition"/>
        <result property="delFlag" column="del_flag"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="isupload" column="isupload"/>
        <result property="uploadTime" column="upload_time"/>
        <result property="orgid" column="orgid"/>
        <result property="pguid" column="pguid"/>
        <result property="guid" column="guid"/>
        <result property="pid" column="pid"/>
    </resultMap>
    <sql id="selectBaseDictOperationVo">
        select id,
               opid,
               opcode,
               opdesc,
               incitypecode,
               incitypedesc,
               oplevelcode,
               opleveldesc,
               opposition,
               del_flag,
               update_by,
               update_time,
               create_by,
               create_time,
               isupload,
               upload_time,
               orgid,
               pguid,
               guid,
               pid
        from base_dict_operation
    </sql>
    <select id="selectBaseDictOperationList" parameterType="com.ruoyi.othervisit.domain.BaseDictOperation"
            resultMap="BaseDictOperationResult">
        <include refid="selectBaseDictOperationVo"/>
        where 1=1
            <if test="opid != null ">
                and opid = #{opid}
            </if>
            <if test="opcode != null  and opcode != ''">
                and opcode = #{opcode}
            </if>
            <if test="opdesc != null  and opdesc != ''">
                and opdesc like concat('%', #{opdesc}, '%')
            </if>
            <if test="incitypecode != null  and incitypecode != ''">
                and incitypecode = #{incitypecode}
            </if>
            <if test="incitypedesc != null  and incitypedesc != ''">
                and incitypedesc = #{incitypedesc}
            </if>
            <if test="oplevelcode != null  and oplevelcode != ''">
                and oplevelcode = #{oplevelcode}
            </if>
            <if test="opleveldesc != null  and opleveldesc != ''">
                and opleveldesc = #{opleveldesc}
            </if>
            <if test="opposition != null  and opposition != ''">
                and opposition = #{opposition}
            </if>
            <if test="isupload != null ">
                and isupload = #{isupload}
            </if>
            <if test="uploadTime != null ">
                and upload_time = #{uploadTime}
            </if>
            <if test="orgid != null  and orgid != ''">
                and orgid = #{orgid}
            </if>
            <if test="pguid != null  and pguid != ''">
                and pguid = #{pguid}
            </if>
            <if test="guid != null  and guid != ''">
                and guid = #{guid}
            </if>
            <if test="pid != null ">
                and pid = #{pid}
            </if>
    </select>
    <select id="selectBaseDictOperationById" parameterType="Long"
            resultMap="BaseDictOperationResult">
        <include refid="selectBaseDictOperationVo"/>
        where id = #{id}
    </select>
    <insert id="insertBaseDictOperation" parameterType="com.ruoyi.othervisit.domain.BaseDictOperation">
        insert into base_dict_operation
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="id != null">id,
            </if>
            <if test="opid != null">opid,
            </if>
            <if test="opcode != null">opcode,
            </if>
            <if test="opdesc != null">opdesc,
            </if>
            <if test="incitypecode != null">incitypecode,
            </if>
            <if test="incitypedesc != null">incitypedesc,
            </if>
            <if test="oplevelcode != null">oplevelcode,
            </if>
            <if test="opleveldesc != null">opleveldesc,
            </if>
            <if test="opposition != null">opposition,
            </if>
            <if test="delFlag != null">del_flag,
            </if>
            <if test="updateBy != null">update_by,
            </if>
            <if test="updateTime != null">update_time,
            </if>
            <if test="createBy != null">create_by,
            </if>
            <if test="createTime != null">create_time,
            </if>
            <if test="isupload != null">isupload,
            </if>
            <if test="uploadTime != null">upload_time,
            </if>
            <if test="orgid != null">orgid,
            </if>
            <if test="pguid != null">pguid,
            </if>
            <if test="guid != null">guid,
            </if>
            <if test="pid != null">pid,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="id != null">#{id},
            </if>
            <if test="opid != null">#{opid},
            </if>
            <if test="opcode != null">#{opcode},
            </if>
            <if test="opdesc != null">#{opdesc},
            </if>
            <if test="incitypecode != null">#{incitypecode},
            </if>
            <if test="incitypedesc != null">#{incitypedesc},
            </if>
            <if test="oplevelcode != null">#{oplevelcode},
            </if>
            <if test="opleveldesc != null">#{opleveldesc},
            </if>
            <if test="opposition != null">#{opposition},
            </if>
            <if test="delFlag != null">#{delFlag},
            </if>
            <if test="updateBy != null">#{updateBy},
            </if>
            <if test="updateTime != null">#{updateTime},
            </if>
            <if test="createBy != null">#{createBy},
            </if>
            <if test="createTime != null">#{createTime},
            </if>
            <if test="isupload != null">#{isupload},
            </if>
            <if test="uploadTime != null">#{uploadTime},
            </if>
            <if test="orgid != null">#{orgid},
            </if>
            <if test="pguid != null">#{pguid},
            </if>
            <if test="guid != null">#{guid},
            </if>
            <if test="pid != null">#{pid},
            </if>
        </trim>
    </insert>
    <update id="updateBaseDictOperation" parameterType="com.ruoyi.othervisit.domain.BaseDictOperation">
        update base_dict_operation
        <trim prefix="SET" suffixOverrides=",">
            <if test="opid != null">opid =
                #{opid},
            </if>
            <if test="opcode != null">opcode =
                #{opcode},
            </if>
            <if test="opdesc != null">opdesc =
                #{opdesc},
            </if>
            <if test="incitypecode != null">incitypecode =
                #{incitypecode},
            </if>
            <if test="incitypedesc != null">incitypedesc =
                #{incitypedesc},
            </if>
            <if test="oplevelcode != null">oplevelcode =
                #{oplevelcode},
            </if>
            <if test="opleveldesc != null">opleveldesc =
                #{opleveldesc},
            </if>
            <if test="opposition != null">opposition =
                #{opposition},
            </if>
            <if test="delFlag != null">del_flag =
                #{delFlag},
            </if>
            <if test="updateBy != null">update_by =
                #{updateBy},
            </if>
            <if test="updateTime != null">update_time =
                #{updateTime},
            </if>
            <if test="createBy != null">create_by =
                #{createBy},
            </if>
            <if test="createTime != null">create_time =
                #{createTime},
            </if>
            <if test="isupload != null">isupload =
                #{isupload},
            </if>
            <if test="uploadTime != null">upload_time =
                #{uploadTime},
            </if>
            <if test="orgid != null">orgid =
                #{orgid},
            </if>
            <if test="pguid != null">pguid =
                #{pguid},
            </if>
            <if test="guid != null">guid =
                #{guid},
            </if>
            <if test="pid != null">pid =
                #{pid},
            </if>
        </trim>
        where id = #{id}
    </update>
    <update id="deleteBaseDictOperationById" parameterType="Long">
        update base_dict_operation
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id = #{id}
    </update>
    <update id="deleteBaseDictOperationByIds" parameterType="String">
        update base_dict_operation
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </update>
</mapper>
ruoyi-othervisit/src/main/resources/mapper/othervisit/PatMedOperationItemMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,792 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.othervisit.mapper.PatMedOperationItemMapper">
    <resultMap type="com.ruoyi.othervisit.domain.PatMedOperationItem" id="PatMedOperationItemResult">
            <result property="id" column="id"/>
            <result property="operationid" column="operationid"/>
            <result property="opid" column="opid"/>
            <result property="opcode" column="opcode"/>
            <result property="opdesc" column="opdesc"/>
            <result property="mainFlag" column="main_flag"/>
            <result property="incitypecode" column="incitypecode"/>
            <result property="incitypedesc" column="incitypedesc"/>
            <result property="oplevelcode" column="oplevelcode"/>
            <result property="opleveldesc" column="opleveldesc"/>
            <result property="opposition" column="opposition"/>
            <result property="delFlag" column="del_flag"/>
            <result property="updateBy" column="update_by"/>
            <result property="updateTime" column="update_time"/>
            <result property="createBy" column="create_by"/>
            <result property="createTime" column="create_time"/>
            <result property="isupload" column="isupload"/>
            <result property="uploadTime" column="upload_time"/>
            <result property="orgid" column="orgid"/>
            <result property="pguid" column="pguid"/>
            <result property="guid" column="guid"/>
            <result property="pid" column="pid"/>
            <result property="opercheckFlag" column="opercheck_flag"/>
            <result property="longTaskReason" column="long_task_reason"/>
            <result property="inhospstate" column="inhospstate"/>
            <result property="inhospEndtime" column="inhosp_endtime"/>
            <result property="serialnum" column="serialnum"/>
            <result property="deptcode" column="deptcode"/>
            <result property="deptname" column="deptname"/>
            <result property="hospitaldistrictcode" column="hospitaldistrictcode"/>
            <result property="hospitaldistrictname" column="hospitaldistrictname"/>
            <result property="leaveldeptcode" column="leaveldeptcode"/>
            <result property="leaveldeptname" column="leaveldeptname"/>
            <result property="leavehospitaldistrictcode" column="leavehospitaldistrictcode"/>
            <result property="leavehospitaldistrictname" column="leavehospitaldistrictname"/>
            <result property="patid" column="patid"/>
            <result property="drcode" column="drcode"/>
            <result property="drname" column="drname"/>
            <result property="nurseId" column="nurse_id"/>
            <result property="nurseName" column="nurse_name"/>
    </resultMap>
    <sql id="selectPatMedOperationItemVo">
        select id, operationid, opid, opcode, opdesc, main_flag, incitypecode, incitypedesc, oplevelcode, opleveldesc, opposition, del_flag, update_by, update_time, create_by, create_time, isupload, upload_time, orgid, pguid, guid, pid, opercheck_flag, long_task_reason, inhospstate, serialnum
        from pat_med_operation_item
    </sql>
    <select id="selectPatMedOperationItemList" parameterType="com.ruoyi.othervisit.domain.PatMedOperationItem"
            resultMap="PatMedOperationItemResult">
        <include refid="selectPatMedOperationItemVo"/>
        where 1=1
                        <if test="operationid != null ">
                            and operationid = #{operationid}
                        </if>
                        <if test="opid != null ">
                            and opid = #{opid}
                        </if>
                        <if test="opcode != null  and opcode != ''">
                            and opcode = #{opcode}
                        </if>
                        <if test="opdesc != null  and opdesc != ''">
                            and opdesc = #{opdesc}
                        </if>
                        <if test="mainFlag != null  and mainFlag != ''">
                            and main_flag = #{mainFlag}
                        </if>
                        <if test="incitypecode != null  and incitypecode != ''">
                            and incitypecode = #{incitypecode}
                        </if>
                        <if test="incitypedesc != null  and incitypedesc != ''">
                            and incitypedesc = #{incitypedesc}
                        </if>
                        <if test="oplevelcode != null  and oplevelcode != ''">
                            and oplevelcode = #{oplevelcode}
                        </if>
                        <if test="opleveldesc != null  and opleveldesc != ''">
                            and opleveldesc = #{opleveldesc}
                        </if>
                        <if test="opposition != null  and opposition != ''">
                            and opposition = #{opposition}
                        </if>
                        <if test="isupload != null ">
                            and isupload = #{isupload}
                        </if>
                        <if test="uploadTime != null ">
                            and upload_time = #{uploadTime}
                        </if>
                        <if test="orgid != null  and orgid != ''">
                            and orgid = #{orgid}
                        </if>
                        <if test="pguid != null  and pguid != ''">
                            and pguid = #{pguid}
                        </if>
                        <if test="guid != null  and guid != ''">
                            and guid = #{guid}
                        </if>
                        <if test="pid != null ">
                            and pid = #{pid}
                        </if>
                        <if test="inhospstate != null  and inhospstate != ''">
                            and inhospstate = #{inhospstate}
                        </if>
                        <if test="serialnum != null  and serialnum != ''">
                            and serialnum = #{serialnum}
                        </if>
    </select>
    <select id="selectPatMedOperationItemById" parameterType="Long"
            resultMap="PatMedOperationItemResult">
            <include refid="selectPatMedOperationItemVo"/>
            where id = #{id}
    </select>
    <insert id="insertPatMedOperationItem" parameterType="com.ruoyi.othervisit.domain.PatMedOperationItem" useGeneratedKeys="true" keyProperty="id">
        insert into pat_med_operation_item
        <trim prefix="(" suffix=")" suffixOverrides=",">
                    <if test="operationid != null">operationid,
                    </if>
                    <if test="opid != null">opid,
                    </if>
                    <if test="opcode != null">opcode,
                    </if>
                    <if test="opdesc != null">opdesc,
                    </if>
                    <if test="mainFlag != null">main_flag,
                    </if>
                    <if test="incitypecode != null">incitypecode,
                    </if>
                    <if test="incitypedesc != null">incitypedesc,
                    </if>
                    <if test="oplevelcode != null">oplevelcode,
                    </if>
                    <if test="opleveldesc != null">opleveldesc,
                    </if>
                    <if test="opposition != null">opposition,
                    </if>
                    <if test="delFlag != null">del_flag,
                    </if>
                    <if test="updateBy != null">update_by,
                    </if>
                    <if test="updateTime != null">update_time,
                    </if>
                    <if test="createBy != null">create_by,
                    </if>
                    <if test="createTime != null">create_time,
                    </if>
                    <if test="isupload != null">isupload,
                    </if>
                    <if test="uploadTime != null">upload_time,
                    </if>
                    <if test="orgid != null">orgid,
                    </if>
                    <if test="pguid != null">pguid,
                    </if>
                    <if test="guid != null">guid,
                    </if>
                    <if test="pid != null">pid,
                    </if>
                    <if test="opercheckFlag != null">opercheck_flag,
                    </if>
                    <if test="longTaskReason != null">long_task_reason,
                    </if>
                    <if test="inhospstate != null">inhospstate,
                    </if>
                    <if test="serialnum != null">serialnum,
                    </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
                    <if test="operationid != null">#{operationid},
                    </if>
                    <if test="opid != null">#{opid},
                    </if>
                    <if test="opcode != null">#{opcode},
                    </if>
                    <if test="opdesc != null">#{opdesc},
                    </if>
                    <if test="mainFlag != null">#{mainFlag},
                    </if>
                    <if test="incitypecode != null">#{incitypecode},
                    </if>
                    <if test="incitypedesc != null">#{incitypedesc},
                    </if>
                    <if test="oplevelcode != null">#{oplevelcode},
                    </if>
                    <if test="opleveldesc != null">#{opleveldesc},
                    </if>
                    <if test="opposition != null">#{opposition},
                    </if>
                    <if test="delFlag != null">#{delFlag},
                    </if>
                    <if test="updateBy != null">#{updateBy},
                    </if>
                    <if test="updateTime != null">#{updateTime},
                    </if>
                    <if test="createBy != null">#{createBy},
                    </if>
                    <if test="createTime != null">#{createTime},
                    </if>
                    <if test="isupload != null">#{isupload},
                    </if>
                    <if test="uploadTime != null">#{uploadTime},
                    </if>
                    <if test="orgid != null">#{orgid},
                    </if>
                    <if test="pguid != null">#{pguid},
                    </if>
                    <if test="guid != null">#{guid},
                    </if>
                    <if test="pid != null">#{pid},
                    </if>
                    <if test="opercheckFlag != null">#{opercheckFlag},
                    </if>
                    <if test="longTaskReason != null">#{longTaskReason},
                    </if>
                    <if test="inhospstate != null">#{inhospstate},
                    </if>
                    <if test="serialnum != null">#{serialnum},
                    </if>
        </trim>
    </insert>
    <update id="updatePatMedOperationItem" parameterType="com.ruoyi.othervisit.domain.PatMedOperationItem">
        update pat_med_operation_item
        <trim prefix="SET" suffixOverrides=",">
                    <if test="operationid != null">operationid =
                        #{operationid},
                    </if>
                    <if test="opid != null">opid =
                        #{opid},
                    </if>
                    <if test="opcode != null">opcode =
                        #{opcode},
                    </if>
                    <if test="opdesc != null">opdesc =
                        #{opdesc},
                    </if>
                    <if test="mainFlag != null">main_flag =
                        #{mainFlag},
                    </if>
                    <if test="incitypecode != null">incitypecode =
                        #{incitypecode},
                    </if>
                    <if test="incitypedesc != null">incitypedesc =
                        #{incitypedesc},
                    </if>
                    <if test="oplevelcode != null">oplevelcode =
                        #{oplevelcode},
                    </if>
                    <if test="opleveldesc != null">opleveldesc =
                        #{opleveldesc},
                    </if>
                    <if test="opposition != null">opposition =
                        #{opposition},
                    </if>
                    <if test="delFlag != null">del_flag =
                        #{delFlag},
                    </if>
                    <if test="updateBy != null">update_by =
                        #{updateBy},
                    </if>
                    <if test="updateTime != null">update_time =
                        #{updateTime},
                    </if>
                    <if test="createBy != null">create_by =
                        #{createBy},
                    </if>
                    <if test="createTime != null">create_time =
                        #{createTime},
                    </if>
                    <if test="isupload != null">isupload =
                        #{isupload},
                    </if>
                    <if test="uploadTime != null">upload_time =
                        #{uploadTime},
                    </if>
                    <if test="orgid != null">orgid =
                        #{orgid},
                    </if>
                    <if test="pguid != null">pguid =
                        #{pguid},
                    </if>
                    <if test="guid != null">guid =
                        #{guid},
                    </if>
                    <if test="pid != null">pid =
                        #{pid},
                    </if>
                    <if test="opercheckFlag != null">opercheck_flag =
                        #{opercheckFlag},
                    </if>
                    <if test="longTaskReason != null">long_task_reason =
                        #{longTaskReason},
                    </if>
                    <if test="inhospstate != null">inhospstate =
                        #{inhospstate},
                    </if>
                    <if test="serialnum != null">serialnum =
                        #{serialnum},
                    </if>
        </trim>
        where id = #{id}
    </update>
    <update id="updatePatMedOperationItemByOperationid" parameterType="com.ruoyi.othervisit.domain.PatMedOperationItem">
        update pat_med_operation_item
        <trim prefix="SET" suffixOverrides=",">
            <if test="opid != null">opid =
                #{opid},
            </if>
            <if test="opcode != null">opcode =
                #{opcode},
            </if>
            <if test="opdesc != null">opdesc =
                #{opdesc},
            </if>
            <if test="mainFlag != null">main_flag =
                #{mainFlag},
            </if>
            <if test="incitypecode != null">incitypecode =
                #{incitypecode},
            </if>
            <if test="incitypedesc != null">incitypedesc =
                #{incitypedesc},
            </if>
            <if test="oplevelcode != null">oplevelcode =
                #{oplevelcode},
            </if>
            <if test="opleveldesc != null">opleveldesc =
                #{opleveldesc},
            </if>
            <if test="opposition != null">opposition =
                #{opposition},
            </if>
            <if test="delFlag != null">del_flag =
                #{delFlag},
            </if>
            <if test="updateBy != null">update_by =
                #{updateBy},
            </if>
            <if test="updateTime != null">update_time =
                #{updateTime},
            </if>
            <if test="createBy != null">create_by =
                #{createBy},
            </if>
            <if test="createTime != null">create_time =
                #{createTime},
            </if>
            <if test="isupload != null">isupload =
                #{isupload},
            </if>
            <if test="uploadTime != null">upload_time =
                #{uploadTime},
            </if>
            <if test="orgid != null">orgid =
                #{orgid},
            </if>
            <if test="pguid != null">pguid =
                #{pguid},
            </if>
            <if test="guid != null">guid =
                #{guid},
            </if>
            <if test="pid != null">pid =
                #{pid},
            </if>
            <if test="opercheckFlag != null">opercheck_flag =
                #{opercheckFlag},
            </if>
            <if test="longTaskReason != null">long_task_reason =
                #{longTaskReason},
            </if>
            <if test="inhospstate != null">inhospstate =
                #{inhospstate},
            </if>
            <if test="serialnum != null">serialnum =
                #{serialnum},
            </if>
        </trim>
        where operationid = #{operationid}
        and opcode = #{opcode}
    </update>
    <insert id="insertPatMedOperationItems">
        insert into pat_med_operation_item
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="operationid != null">operationid,
            </if>
            <if test="opid != null">opid,
            </if>
            <if test="opcode != null">opcode,
            </if>
            <if test="opdesc != null">opdesc,
            </if>
            <if test="mainFlag != null">main_flag,
            </if>
            <if test="incitypecode != null">incitypecode,
            </if>
            <if test="incitypedesc != null">incitypedesc,
            </if>
            <if test="oplevelcode != null">oplevelcode,
            </if>
            <if test="opleveldesc != null">opleveldesc,
            </if>
            <if test="opposition != null">opposition,
            </if>
            <if test="delFlag != null">del_flag,
            </if>
            <if test="updateBy != null">update_by,
            </if>
            <if test="updateTime != null">update_time,
            </if>
            <if test="createBy != null">create_by,
            </if>
            <if test="createTime != null">create_time,
            </if>
            <if test="isupload != null">isupload,
            </if>
            <if test="uploadTime != null">upload_time,
            </if>
            <if test="orgid != null">orgid,
            </if>
            <if test="pguid != null">pguid,
            </if>
            <if test="guid != null">guid,
            </if>
            <if test="pid != null">pid,
            </if>
            <if test="opercheckFlag != null">opercheck_flag,
            </if>
            <if test="longTaskReason != null">long_task_reason,
            </if>
            <if test="inhospstate != null">inhospstate,
            </if>
            <if test="serialnum != null">serialnum,
            </if>
        </trim>
        values
        <foreach item="item" index="index" collection="list" separator=",">
            <trim prefix="(" suffix=")" suffixOverrides=",">
                <if test="operationid != null">#{item.operationid},
                </if>
                <if test="opid != null">#{item.opid},
                </if>
                <if test="opcode != null">#{item.opcode},
                </if>
                <if test="opdesc != null">#{item.opdesc},
                </if>
                <if test="mainFlag != null">#{item.mainFlag},
                </if>
                <if test="incitypecode != null">#{item.incitypecode},
                </if>
                <if test="incitypedesc != null">#{item.incitypedesc},
                </if>
                <if test="oplevelcode != null">#{item.oplevelcode},
                </if>
                <if test="opleveldesc != null">#{item.opleveldesc},
                </if>
                <if test="opposition != null">#{item.opposition},
                </if>
                <if test="delFlag != null">#{item.delFlag},
                </if>
                <if test="updateBy != null">#{item.updateBy},
                </if>
                <if test="updateTime != null">#{item.updateTime},
                </if>
                <if test="createBy != null">#{item.createBy},
                </if>
                <if test="createTime != null">#{item.createTime},
                </if>
                <if test="isupload != null">#{item.isupload},
                </if>
                <if test="uploadTime != null">#{item.uploadTime},
                </if>
                <if test="orgid != null">#{item.orgid},
                </if>
                <if test="pguid != null">#{item.pguid},
                </if>
                <if test="guid != null">#{item.guid},
                </if>
                <if test="pid != null">#{item.pid},
                </if>
                <if test="opercheckFlag != null">#{item.opercheckFlag},
                </if>
                <if test="longTaskReason != null">#{item.longTaskReason},
                </if>
                <if test="inhospstate != null">#{item.inhospstate},
                </if>
                <if test="serialnum != null">#{item.serialnum},
                </if>
            </trim>
        </foreach>
    </insert>
    <update id="deletePatMedOperationItemById" parameterType="Long">
        update pat_med_operation_item
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where  id = #{id}
    </update>
    <update id="deletePatMedOperationItemByIds" parameterType="String">
        update  pat_med_operation_item
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </update>
    <delete id="deleteHardPatMedOperationItemByOpId" parameterType="String">
        delete from pat_med_operation_item
               where opid = #{opid}
               and opercheck_flag != 1
    </delete>
    <update id="deletePatMedOperationItemByOperationid" parameterType="Long">
        update  pat_med_operation_item
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where operationid = #{operationid}
        and opercheck_flag != 1
    </update>
    <update id="deleteSoftPatMedOperationItemByOpId" parameterType="String">
        update pat_med_operation_item
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where opid = #{opid}
    </update>
    <select id="selectOperationItemList" parameterType="com.ruoyi.othervisit.domain.PatMedOperationItem"
            resultMap="PatMedOperationItemResult">
        select pmoi.id, pmoi.operationid, pmoi.opid, pmoi.opcode, pmoi.opdesc, pmoi.main_flag, pmoi.incitypecode,
        pmoi.incitypedesc, pmoi.oplevelcode, pmoi.opleveldesc, pmoi.opposition, pmoi.del_flag, pmoi.update_by,
        pmoi.update_time, pmoi.create_by, pmoi.create_time, pmoi.isupload, pmoi.upload_time, pmoi.orgid,
        pmoi.pguid, pmoi.guid, pmoi.pid, pmoi.opercheck_flag, pmoi.long_task_reason,
        pmo.deptcode, pmo.deptname, pmo.hospitaldistrictcode, pmo.hospitaldistrictname,
        pmo.leaveldeptcode, pmo.leaveldeptname, pmo.leavehospitaldistrictcode, pmo.leavehospitaldistrictname, pmo.patid,
        pmo.drcode, pmo.drname, pmo.nurse_id, pmo.nurse_name,
        pmo.serialnum, pmi.inhospstate
        from pat_med_operation pmo
        join pat_med_operation_item pmoi
        on pmoi.opid = pmo.opid
        join pat_med_inhosp pmi
        on pmo.serialnum = pmi.serialnum
        where
        pmo.del_flag = '0'
        and pmoi.del_flag = '0'
        and pmo.opid is not null
        and pmoi.opid is not null
        <if test="operationid != null ">
            and pmoi.operationid = #{operationid}
        </if>
        <if test="opid != null ">
            and pmoi.opid = #{opid}
        </if>
        <if test="opcode != null  and opcode != ''">
            and pmoi.opcode = #{opcode}
        </if>
        <if test="opdesc != null  and opdesc != ''">
            and pmoi.opdesc = #{opdesc}
        </if>
        <if test="mainFlag != null  and mainFlag != ''">
            and pmoi.main_flag = #{mainFlag}
        </if>
        <if test="incitypecode != null  and incitypecode != ''">
            and pmoi.incitypecode = #{incitypecode}
        </if>
        <if test="incitypedesc != null  and incitypedesc != ''">
            and pmoi.incitypedesc = #{incitypedesc}
        </if>
        <if test="oplevelcode != null  and oplevelcode != ''">
            and pmoi.oplevelcode = #{oplevelcode}
        </if>
        <if test="opleveldesc != null  and opleveldesc != ''">
            and pmoi.opleveldesc = #{opleveldesc}
        </if>
        <if test="opposition != null  and opposition != ''">
            and pmoi.opposition = #{opposition}
        </if>
        <if test="isupload != null ">
            and pmoi.isupload = #{isupload}
        </if>
        <if test="uploadTime != null ">
            and pmoi.upload_time = #{uploadTime}
        </if>
        <if test="orgid != null  and orgid != ''">
            and pmoi.orgid = #{orgid}
        </if>
        <if test="pguid != null  and pguid != ''">
            and pmoi.pguid = #{pguid}
        </if>
        <if test="guid != null  and guid != ''">
            and pmoi.guid = #{guid}
        </if>
        <if test="pid != null ">
            and pmoi.pid = #{pid}
        </if>
        <if test="inhospstate != null and inhospstate != ''">
            and pmi.inhospstate = #{inhospstate}
            <if test="inhospstate == '1'">
                and pmi.endtime is not null
            </if>
        </if>
        <if test="serialnum != null and serialnum != ''">
            and pmo.serialnum = #{serialnum}
        </if>
        <if test="deptcode != null and deptcode != ''">
            and pmo.deptcode = #{deptcode}
        </if>
        <if test="deptname != null and deptname != ''">
            and pmo.deptname = #{deptname}
        </if>
        <if test="hospitaldistrictcode != null and hospitaldistrictcode != ''">
            and pmo.hospitaldistrictcode = #{hospitaldistrictcode}
        </if>
        <if test="hospitaldistrictname != null and hospitaldistrictname != ''">
            and pmo.hospitaldistrictname = #{hospitaldistrictname}
        </if>
        <if test="patid != null">
            and pmo.patid = #{patid}
        </if>
        <if test="deptCodes != null and deptCodes.size()>0">
            AND pmo.deptcode IN
            <foreach collection="deptCodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="wardCodes != null and wardCodes.size()>0">
            AND pmo.hospitaldistrictcode IN
            <foreach collection="wardCodes" item="wardCode" open="(" separator=","
                     close=")">
                #{wardCode}
            </foreach>
        </if>
        <if test="opLevelCodes != null and opLevelCodes.size()>0">
            AND pmoi.oplevelcode IN
            <foreach collection="opLevelCodes" item="levelCode" open="(" separator=","
                     close=")">
                #{levelCode}
            </foreach>
        </if>
        <if test="opercheckFlag != null">
            and pmoi.opercheck_flag = #{opercheckFlag}
        </if>
    </select>
    <select id="selectOperationItemListMaxLevel" parameterType="com.ruoyi.othervisit.domain.PatMedOperationItem"
            resultMap="PatMedOperationItemResult">
        select pmoi.id, pmoi.operationid, pmoi.opid, pmoi.opcode, pmoi.opdesc, pmoi.main_flag, pmoi.incitypecode,
        pmoi.incitypedesc, pmoi.oplevelcode, pmoi.opleveldesc, pmoi.opposition, pmoi.del_flag, pmoi.update_by,
        pmoi.update_time, pmoi.create_by, pmoi.create_time, pmoi.isupload, pmoi.upload_time, pmoi.orgid,
        pmoi.pguid, pmoi.guid, pmoi.pid, pmoi.opercheck_flag, pmoi.long_task_reason,
        pmo.deptcode, pmo.deptname, pmo.hospitaldistrictcode, pmo.hospitaldistrictname,
        pmo.leaveldeptcode, pmo.leaveldeptname, pmo.leavehospitaldistrictcode, pmo.leavehospitaldistrictname, pmo.patid,
        pmo.drcode, pmo.drname, pmo.nurse_id, pmo.nurse_name,
        pmo.serialnum, pmi.inhospstate
        from pat_med_operation pmo
        join pat_med_operation_item pmoi
        on pmoi.opid = pmo.opid
        join (
            select opid, max(oplevelcode) as max_oplevelcode
            from pat_med_operation_item
            where del_flag = '0'
              and opid is not null
              and oplevelcode is not null
            group by opid
        ) pmoi_max
        on pmoi_max.opid = pmoi.opid
        and (pmoi.oplevelcode = pmoi_max.max_oplevelcode
             or (pmoi.oplevelcode is null and pmoi_max.max_oplevelcode is null))
        join pat_med_inhosp pmi
        on pmo.serialnum = pmi.serialnum
        where
        pmo.del_flag = '0'
        and pmoi.del_flag = '0'
        and pmo.opid is not null
        and pmoi.opid is not null
        <if test="operationid != null ">
            and pmoi.operationid = #{operationid}
        </if>
        <if test="opid != null ">
            and pmoi.opid = #{opid}
        </if>
        <if test="opcode != null  and opcode != ''">
            and pmoi.opcode = #{opcode}
        </if>
        <if test="opdesc != null  and opdesc != ''">
            and pmoi.opdesc = #{opdesc}
        </if>
        <if test="mainFlag != null  and mainFlag != ''">
            and pmoi.main_flag = #{mainFlag}
        </if>
        <if test="incitypecode != null  and incitypecode != ''">
            and pmoi.incitypecode = #{incitypecode}
        </if>
        <if test="incitypedesc != null  and incitypedesc != ''">
            and pmoi.incitypedesc = #{incitypedesc}
        </if>
        <if test="oplevelcode != null  and oplevelcode != ''">
            and pmoi.oplevelcode = #{oplevelcode}
        </if>
        <if test="opleveldesc != null  and opleveldesc != ''">
            and pmoi.opleveldesc = #{opleveldesc}
        </if>
        <if test="opposition != null  and opposition != ''">
            and pmoi.opposition = #{opposition}
        </if>
        <if test="isupload != null ">
            and pmoi.isupload = #{isupload}
        </if>
        <if test="uploadTime != null ">
            and pmoi.upload_time = #{uploadTime}
        </if>
        <if test="orgid != null  and orgid != ''">
            and pmoi.orgid = #{orgid}
        </if>
        <if test="pguid != null  and pguid != ''">
            and pmoi.pguid = #{pguid}
        </if>
        <if test="guid != null  and guid != ''">
            and pmoi.guid = #{guid}
        </if>
        <if test="pid != null ">
            and pmoi.pid = #{pid}
        </if>
        <if test="inhospstate != null and inhospstate != ''">
            and pmi.inhospstate = #{inhospstate}
            <if test="inhospstate == '1'">
                and pmi.endtime is not null
            </if>
        </if>
        <if test="serialnum != null and serialnum != ''">
            and pmo.serialnum = #{serialnum}
        </if>
        <if test="deptcode != null and deptcode != ''">
            and pmo.deptcode = #{deptcode}
        </if>
        <if test="deptname != null and deptname != ''">
            and pmo.deptname = #{deptname}
        </if>
        <if test="hospitaldistrictcode != null and hospitaldistrictcode != ''">
            and pmo.hospitaldistrictcode = #{hospitaldistrictcode}
        </if>
        <if test="hospitaldistrictname != null and hospitaldistrictname != ''">
            and pmo.hospitaldistrictname = #{hospitaldistrictname}
        </if>
        <if test="patid != null">
            and pmo.patid = #{patid}
        </if>
        <if test="deptCodes != null and deptCodes.size()>0">
            AND pmo.deptcode IN
            <foreach collection="deptCodes" item="deptcode" open="(" separator=","
                     close=")">
                #{deptcode}
            </foreach>
        </if>
        <if test="wardCodes != null and wardCodes.size()>0">
            AND pmo.hospitaldistrictcode IN
            <foreach collection="wardCodes" item="wardCode" open="(" separator=","
                     close=")">
                #{wardCode}
            </foreach>
        </if>
        <if test="opLevelCodes != null and opLevelCodes.size()>0">
            AND pmoi.oplevelcode IN
            <foreach collection="opLevelCodes" item="levelCode" open="(" separator=","
                     close=")">
                #{levelCode}
            </foreach>
        </if>
        <if test="opercheckFlag != null">
            and pmoi.opercheck_flag = #{opercheckFlag}
        </if>
    </select>
</mapper>
ruoyi-othervisit/src/main/resources/mapper/othervisit/PatMedOperationMapper.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,940 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.othervisit.mapper.PatMedOperationMapper">
    <resultMap type="com.ruoyi.othervisit.domain.PatMedOperation" id="PatMedOperationResult">
        <result property="id" column="id"/>
        <result property="patid" column="patid"/>
        <result property="patcode" column="patcode"/>
        <result property="paname" column="paname"/>
        <result property="opreqid" column="opreqid"/>
        <result property="opreqcode" column="opreqcode"/>
        <result property="opsstatus" column="opsstatus"/>
        <result property="optypecode" column="optypecode"/>
        <result property="optypename" column="optypename"/>
        <result property="operatortime" column="operatortime"/>
        <result property="opreqtime" column="opreqtime"/>
        <result property="reqdoccode" column="reqdoccode"/>
        <result property="reqdocname" column="reqdocname"/>
        <result property="reqdeptcode" column="reqdeptcode"/>
        <result property="reqdeptname" column="reqdeptname"/>
        <result property="reqloccode" column="reqloccode"/>
        <result property="reqlocname" column="reqlocname"/>
        <result property="remark" column="remark"/>
        <result property="surgeoncode" column="surgeoncode"/>
        <result property="surgeonname" column="surgeonname"/>
        <result property="firasscode" column="firasscode"/>
        <result property="firassname" column="firassname"/>
        <result property="secasscode" column="secasscode"/>
        <result property="secassname" column="secassname"/>
        <result property="thrasscode" column="thrasscode"/>
        <result property="thrassname" column="thrassname"/>
        <result property="canceltime" column="canceltime"/>
        <result property="cancelreason" column="cancelreason"/>
        <result property="cancelcode" column="cancelcode"/>
        <result property="cancelname" column="cancelname"/>
        <result property="visittype" column="visittype"/>
        <result property="status" column="status"/>
        <result property="schemetime" column="schemetime"/>
        <result property="schemestatus" column="schemestatus"/>
        <result property="delFlag" column="del_flag"/>
        <result property="updateBy" column="update_by"/>
        <result property="updateTime" column="update_time"/>
        <result property="createBy" column="create_by"/>
        <result property="createTime" column="create_time"/>
        <result property="isupload" column="isupload"/>
        <result property="uploadTime" column="upload_time"/>
        <result property="orgid" column="orgid"/>
        <result property="guid" column="guid"/>
        <result property="pguid" column="pguid"/>
        <result property="pid" column="pid"/>
        <result property="inhospstate" column="inhospstate"/>
        <result property="patno" column="patno"/>
        <result property="patname" column="patname"/>
        <result property="visitid" column="visitid"/>
        <result property="serialnum" column="serialnum"/>
        <result property="inhospno" column="inhospno"/>
        <result property="hospitalname" column="hospitalname"/>
        <result property="hospitalcode" column="hospitalcode"/>
        <result property="deptid" column="deptid"/>
        <result property="deptcode" column="deptcode"/>
        <result property="deptname" column="deptname"/>
        <result property="hospitaldistrictcode" column="hospitaldistrictcode"/>
        <result property="hospitaldistrictname" column="hospitaldistrictname"/>
        <result property="leaveldeptcode" column="leaveldeptcode"/>
        <result property="leaveldeptname" column="leaveldeptname"/>
        <result property="leavehospitaldistrictcode" column="leavehospitaldistrictcode"/>
        <result property="leavehospitaldistrictname" column="leavehospitaldistrictname"/>
        <result property="roomno" column="roomno"/>
        <result property="bedNo" column="bed_no"/>
        <result property="drcode" column="drcode"/>
        <result property="drname" column="drname"/>
        <result property="nurseId" column="nurse_id"/>
        <result property="nurseName" column="nurse_name"/>
        <result property="checkFlag" column="check_flag"/>
        <result property="longTaskReason" column="long_task_reason"/>
        <result property="opid" column="opid"/>
        <result property="opcode" column="opcode"/>
        <result property="opdesc" column="opdesc"/>
        <result property="oplevelcode" column="oplevelcode"/>
        <result property="opleveldesc" column="opleveldesc"/>
    </resultMap>
    <sql id="selectPatMedOperationVo">
        select id,
               patid,
               patcode,
               paname,
               opreqid,
               opreqcode,
               opsstatus,
               optypecode,
               optypename,
               operatortime,
               opreqtime,
               reqdoccode,
               reqdocname,
               reqdeptcode,
               reqdeptname,
               reqloccode,
               reqlocname,
               remark,
               surgeoncode,
               surgeonname,
               firasscode,
               firassname,
               secasscode,
               secassname,
               thrasscode,
               thrassname,
               canceltime,
               cancelreason,
               cancelcode,
               cancelname,
               visittype,
               status,
               schemetime,
               schemestatus,
               del_flag,
               update_by,
               update_time,
               create_by,
               create_time,
               isupload,
               upload_time,
               orgid,
               guid,
               pguid,
               pid,
               inhospstate,
               patno,
               patname,
               visitid,
               serialnum,
               inhospno,
               hospitalname,
               hospitalcode,
               deptid,
               deptcode,
               deptname,
               hospitaldistrictcode,
               hospitaldistrictname,
               leaveldeptcode,
               leaveldeptname,
               leavehospitaldistrictcode,
               leavehospitaldistrictname,
               roomno,
               bed_no,
               drcode,
               drname,
               nurse_id,
               nurse_name,
               check_flag,
               long_task_reason,
               opid,
               opcode,
               opdesc,
               oplevelcode,
               opleveldesc
        from pat_med_operation
    </sql>
    <select id="selectPatMedOperationList" parameterType="com.ruoyi.othervisit.domain.PatMedOperation"
            resultMap="PatMedOperationResult">
        <include refid="selectPatMedOperationVo"/>
        where 1=1
            and del_flag='0'
            <if test="patid != null ">
                and patid = #{patid}
            </if>
            <if test="patcode != null  and patcode != ''">
                and patcode = #{patcode}
            </if>
            <if test="paname != null  and paname != ''">
                and paname like concat('%', #{paname}, '%')
            </if>
            <if test="opreqid != null ">
                and opreqid = #{opreqid}
            </if>
            <if test="opreqcode != null  and opreqcode != ''">
                and opreqcode = #{opreqcode}
            </if>
            <if test="opsstatus != null  and opsstatus != ''">
                and opsstatus = #{opsstatus}
            </if>
            <if test="optypecode != null  and optypecode != ''">
                and optypecode = #{optypecode}
            </if>
            <if test="optypename != null  and optypename != ''">
                and optypename like concat('%', #{optypename}, '%')
            </if>
            <!--            <if test="operatortime != null ">-->
            <!--                and operatortime = #{operatortime}-->
            <!--            </if>-->
            <if test="beginTime != null ">and date_format(operatortime,'%y%m%d') &gt;=
                date_format(#{beginTime},'%y%m%d')
            </if>
            <if test="endTime != null ">and date_format(operatortime,'%y%m%d') &lt;=
                date_format(#{endTime},'%y%m%d')
            </if>
            <if test="opreqtime != null ">
                and opreqtime = #{opreqtime}
            </if>
            <if test="reqdoccode != null  and reqdoccode != ''">
                and reqdoccode = #{reqdoccode}
            </if>
            <if test="reqdocname != null  and reqdocname != ''">
                and reqdocname like concat('%', #{reqdocname}, '%')
            </if>
            <if test="reqdeptcode != null  and reqdeptcode != ''">
                and reqdeptcode = #{reqdeptcode}
            </if>
            <if test="reqdeptname != null  and reqdeptname != ''">
                and reqdeptname like concat('%', #{reqdeptname}, '%')
            </if>
            <if test="reqloccode != null  and reqloccode != ''">
                and reqloccode = #{reqloccode}
            </if>
            <if test="reqlocname != null  and reqlocname != ''">
                and reqlocname like concat('%', #{reqlocname}, '%')
            </if>
            <if test="surgeoncode != null  and surgeoncode != ''">
                and surgeoncode = #{surgeoncode}
            </if>
            <if test="surgeonname != null  and surgeonname != ''">
                and surgeonname like concat('%', #{surgeonname}, '%')
            </if>
            <if test="firasscode != null  and firasscode != ''">
                and firasscode = #{firasscode}
            </if>
            <if test="firassname != null  and firassname != ''">
                and firassname like concat('%', #{firassname}, '%')
            </if>
            <if test="secasscode != null  and secasscode != ''">
                and secasscode = #{secasscode}
            </if>
            <if test="secassname != null  and secassname != ''">
                and secassname like concat('%', #{secassname}, '%')
            </if>
            <if test="thrasscode != null  and thrasscode != ''">
                and thrasscode = #{thrasscode}
            </if>
            <if test="thrassname != null  and thrassname != ''">
                and thrassname like concat('%', #{thrassname}, '%')
            </if>
            <if test="canceltime != null  and canceltime != ''">
                and canceltime = #{canceltime}
            </if>
            <if test="cancelreason != null  and cancelreason != ''">
                and cancelreason = #{cancelreason}
            </if>
            <if test="cancelcode != null  and cancelcode != ''">
                and cancelcode = #{cancelcode}
            </if>
            <if test="cancelname != null  and cancelname != ''">
                and cancelname like concat('%', #{cancelname}, '%')
            </if>
            <if test="visittype != null ">
                and visittype = #{visittype}
            </if>
            <if test="status != null  and status != ''">
                and status = #{status}
            </if>
            <if test="schemetime != null ">
                and schemetime = #{schemetime}
            </if>
            <if test="schemestatus != null ">
                and schemestatus = #{schemestatus}
            </if>
            <if test="isupload != null ">
                and isupload = #{isupload}
            </if>
            <if test="uploadTime != null ">
                and upload_time = #{uploadTime}
            </if>
            <if test="orgid != null  and orgid != ''">
                and orgid = #{orgid}
            </if>
            <if test="guid != null  and guid != ''">
                and guid = #{guid}
            </if>
            <if test="pguid != null  and pguid != ''">
                and pguid = #{pguid}
            </if>
            <if test="pid != null ">
                and pid = #{pid}
            </if>
            <if test="inhospstate != null  and inhospstate != ''">
                and inhospstate = #{inhospstate}
            </if>
            <if test="patno != null  and patno != ''">
                and patno = #{patno}
            </if>
            <if test="patname != null  and patname != ''">
                and patname like concat('%', #{patname}, '%')
            </if>
            <if test="visitid != null ">
                and visitid = #{visitid}
            </if>
            <if test="serialnum != null  and serialnum != ''">
                and serialnum = #{serialnum}
            </if>
            <if test="inhospno != null  and inhospno != ''">
                and inhospno = #{inhospno}
            </if>
            <if test="hospitalname != null  and hospitalname != ''">
                and hospitalname like concat('%', #{hospitalname}, '%')
            </if>
            <if test="hospitalcode != null  and hospitalcode != ''">
                and hospitalcode = #{hospitalcode}
            </if>
            <if test="deptid != null  and deptid != ''">
                and deptid = #{deptid}
            </if>
            <if test="deptcode != null  and deptcode != ''">
                and deptcode = #{deptcode}
            </if>
            <if test="deptname != null  and deptname != ''">
                and deptname like concat('%', #{deptname}, '%')
            </if>
            <if test="hospitaldistrictcode != null  and hospitaldistrictcode != ''">
                and hospitaldistrictcode = #{hospitaldistrictcode}
            </if>
            <if test="hospitaldistrictname != null  and hospitaldistrictname != ''">
                and hospitaldistrictname like concat('%', #{hospitaldistrictname}, '%')
            </if>
            <if test="leaveldeptcode != null  and leaveldeptcode != ''">
                and leaveldeptcode = #{leaveldeptcode}
            </if>
            <if test="leaveldeptname != null  and leaveldeptname != ''">
                and leaveldeptname like concat('%', #{leaveldeptname}, '%')
            </if>
            <if test="leavehospitaldistrictcode != null  and leavehospitaldistrictcode != ''">
                and leavehospitaldistrictcode = #{leavehospitaldistrictcode}
            </if>
            <if test="leavehospitaldistrictname != null  and leavehospitaldistrictname != ''">
                and leavehospitaldistrictname like concat('%', #{leavehospitaldistrictname}, '%')
            </if>
            <if test="roomno != null  and roomno != ''">
                and roomno = #{roomno}
            </if>
            <if test="bedNo != null  and bedNo != ''">
                and bed_no = #{bedNo}
            </if>
            <if test="drcode != null  and drcode != ''">
                and drcode = #{drcode}
            </if>
            <if test="drname != null  and drname != ''">
                and drname like concat('%', #{drname}, '%')
            </if>
            <if test="nurseId != null  and nurseId != ''">
                and nurse_id = #{nurseId}
            </if>
            <if test="nurseName != null  and nurseName != ''">
                and nurse_name like concat('%', #{nurseName}, '%')
            </if>
            <if test="checkFlag != null  and checkFlag != ''">
                and check_flag = #{checkFlag}
            </if>
            <if test="longTaskReason != null  and longTaskReason != ''">
                and long_task_reason = #{longTaskReason}
            </if>
            <if test="opid != null  and opid != ''">
                and opid = #{opid}
            </if>
            <if test="opcode != null  and opcode != ''">
                and opcode = #{opcode}
            </if>
            <if test="opdesc != null  and opdesc != ''">
                and opdesc = #{opdesc}
            </if>
            <if test="oplevelcode != null  and oplevelcode != ''">
                and oplevelcode = #{oplevelcode}
            </if>
            <if test="opleveldesc != null  and opleveldesc != ''">
                and opleveldesc = #{opleveldesc}
            </if>
    </select>
    <select id="selectPatMedOperationById" parameterType="Long"
            resultMap="PatMedOperationResult">
        <include refid="selectPatMedOperationVo"/>
        where id = #{id}
    </select>
    <insert id="insertPatMedOperation" parameterType="com.ruoyi.othervisit.domain.PatMedOperation" useGeneratedKeys="true" keyProperty="id">
        insert into pat_med_operation
        <trim prefix="(" suffix=")" suffixOverrides=",">
            <if test="patid != null">patid,
            </if>
            <if test="patcode != null">patcode,
            </if>
            <if test="paname != null">paname,
            </if>
            <if test="opreqid != null">opreqid,
            </if>
            <if test="opreqcode != null">opreqcode,
            </if>
            <if test="opsstatus != null">opsstatus,
            </if>
            <if test="optypecode != null">optypecode,
            </if>
            <if test="optypename != null">optypename,
            </if>
            <if test="operatortime != null">operatortime,
            </if>
            <if test="opreqtime != null">opreqtime,
            </if>
            <if test="reqdoccode != null">reqdoccode,
            </if>
            <if test="reqdocname != null">reqdocname,
            </if>
            <if test="reqdeptcode != null">reqdeptcode,
            </if>
            <if test="reqdeptname != null">reqdeptname,
            </if>
            <if test="reqloccode != null">reqloccode,
            </if>
            <if test="reqlocname != null">reqlocname,
            </if>
            <if test="remark != null">remark,
            </if>
            <if test="surgeoncode != null">surgeoncode,
            </if>
            <if test="surgeonname != null">surgeonname,
            </if>
            <if test="firasscode != null">firasscode,
            </if>
            <if test="firassname != null">firassname,
            </if>
            <if test="secasscode != null">secasscode,
            </if>
            <if test="secassname != null">secassname,
            </if>
            <if test="thrasscode != null">thrasscode,
            </if>
            <if test="thrassname != null">thrassname,
            </if>
            <if test="canceltime != null">canceltime,
            </if>
            <if test="cancelreason != null">cancelreason,
            </if>
            <if test="cancelcode != null">cancelcode,
            </if>
            <if test="cancelname != null">cancelname,
            </if>
            <if test="visittype != null">visittype,
            </if>
            <if test="status != null">status,
            </if>
            <if test="schemetime != null">schemetime,
            </if>
            <if test="schemestatus != null">schemestatus,
            </if>
            <if test="delFlag != null">del_flag,
            </if>
            <if test="updateBy != null">update_by,
            </if>
            <if test="updateTime != null">update_time,
            </if>
            <if test="createBy != null">create_by,
            </if>
            <if test="createTime != null">create_time,
            </if>
            <if test="isupload != null">isupload,
            </if>
            <if test="uploadTime != null">upload_time,
            </if>
            <if test="orgid != null">orgid,
            </if>
            <if test="guid != null">guid,
            </if>
            <if test="pguid != null">pguid,
            </if>
            <if test="pid != null">pid,
            </if>
            <if test="inhospstate != null">inhospstate,
            </if>
            <if test="patno != null">patno,
            </if>
            <if test="patname != null">patname,
            </if>
            <if test="visitid != null">visitid,
            </if>
            <if test="serialnum != null">serialnum,
            </if>
            <if test="inhospno != null">inhospno,
            </if>
            <if test="hospitalname != null">hospitalname,
            </if>
            <if test="hospitalcode != null">hospitalcode,
            </if>
            <if test="deptid != null">deptid,
            </if>
            <if test="deptcode != null">deptcode,
            </if>
            <if test="deptname != null">deptname,
            </if>
            <if test="hospitaldistrictcode != null">hospitaldistrictcode,
            </if>
            <if test="hospitaldistrictname != null">hospitaldistrictname,
            </if>
            <if test="leaveldeptcode != null">leaveldeptcode,
            </if>
            <if test="leaveldeptname != null">leaveldeptname,
            </if>
            <if test="leavehospitaldistrictcode != null">leavehospitaldistrictcode,
            </if>
            <if test="leavehospitaldistrictname != null">leavehospitaldistrictname,
            </if>
            <if test="roomno != null">roomno,
            </if>
            <if test="bedNo != null">bed_no,
            </if>
            <if test="drcode != null">drcode,
            </if>
            <if test="drname != null">drname,
            </if>
            <if test="nurseId != null">nurse_id,
            </if>
            <if test="nurseName != null">nurse_name,
            </if>
            <if test="checkFlag != null">check_flag,
            </if>
            <if test="longTaskReason != null">long_task_reason,
            </if>
            <if test="opid != null">opid,
            </if>
            <if test="opcode != null">opcode,
            </if>
            <if test="opdesc != null">opdesc,
            </if>
            <if test="oplevelcode != null">oplevelcode,
            </if>
            <if test="opleveldesc != null">opleveldesc,
            </if>
        </trim>
        <trim prefix="values (" suffix=")" suffixOverrides=",">
            <if test="patid != null">#{patid},
            </if>
            <if test="patcode != null">#{patcode},
            </if>
            <if test="paname != null">#{paname},
            </if>
            <if test="opreqid != null">#{opreqid},
            </if>
            <if test="opreqcode != null">#{opreqcode},
            </if>
            <if test="opsstatus != null">#{opsstatus},
            </if>
            <if test="optypecode != null">#{optypecode},
            </if>
            <if test="optypename != null">#{optypename},
            </if>
            <if test="operatortime != null">#{operatortime},
            </if>
            <if test="opreqtime != null">#{opreqtime},
            </if>
            <if test="reqdoccode != null">#{reqdoccode},
            </if>
            <if test="reqdocname != null">#{reqdocname},
            </if>
            <if test="reqdeptcode != null">#{reqdeptcode},
            </if>
            <if test="reqdeptname != null">#{reqdeptname},
            </if>
            <if test="reqloccode != null">#{reqloccode},
            </if>
            <if test="reqlocname != null">#{reqlocname},
            </if>
            <if test="remark != null">#{remark},
            </if>
            <if test="surgeoncode != null">#{surgeoncode},
            </if>
            <if test="surgeonname != null">#{surgeonname},
            </if>
            <if test="firasscode != null">#{firasscode},
            </if>
            <if test="firassname != null">#{firassname},
            </if>
            <if test="secasscode != null">#{secasscode},
            </if>
            <if test="secassname != null">#{secassname},
            </if>
            <if test="thrasscode != null">#{thrasscode},
            </if>
            <if test="thrassname != null">#{thrassname},
            </if>
            <if test="canceltime != null">#{canceltime},
            </if>
            <if test="cancelreason != null">#{cancelreason},
            </if>
            <if test="cancelcode != null">#{cancelcode},
            </if>
            <if test="cancelname != null">#{cancelname},
            </if>
            <if test="visittype != null">#{visittype},
            </if>
            <if test="status != null">#{status},
            </if>
            <if test="schemetime != null">#{schemetime},
            </if>
            <if test="schemestatus != null">#{schemestatus},
            </if>
            <if test="delFlag != null">#{delFlag},
            </if>
            <if test="updateBy != null">#{updateBy},
            </if>
            <if test="updateTime != null">#{updateTime},
            </if>
            <if test="createBy != null">#{createBy},
            </if>
            <if test="createTime != null">#{createTime},
            </if>
            <if test="isupload != null">#{isupload},
            </if>
            <if test="uploadTime != null">#{uploadTime},
            </if>
            <if test="orgid != null">#{orgid},
            </if>
            <if test="guid != null">#{guid},
            </if>
            <if test="pguid != null">#{pguid},
            </if>
            <if test="pid != null">#{pid},
            </if>
            <if test="inhospstate != null">#{inhospstate},
            </if>
            <if test="patno != null">#{patno},
            </if>
            <if test="patname != null">#{patname},
            </if>
            <if test="visitid != null">#{visitid},
            </if>
            <if test="serialnum != null">#{serialnum},
            </if>
            <if test="inhospno != null">#{inhospno},
            </if>
            <if test="hospitalname != null">#{hospitalname},
            </if>
            <if test="hospitalcode != null">#{hospitalcode},
            </if>
            <if test="deptid != null">#{deptid},
            </if>
            <if test="deptcode != null">#{deptcode},
            </if>
            <if test="deptname != null">#{deptname},
            </if>
            <if test="hospitaldistrictcode != null">#{hospitaldistrictcode},
            </if>
            <if test="hospitaldistrictname != null">#{hospitaldistrictname},
            </if>
            <if test="leaveldeptcode != null">#{leaveldeptcode},
            </if>
            <if test="leaveldeptname != null">#{leaveldeptname},
            </if>
            <if test="leavehospitaldistrictcode != null">#{leavehospitaldistrictcode},
            </if>
            <if test="leavehospitaldistrictname != null">#{leavehospitaldistrictname},
            </if>
            <if test="roomno != null">#{roomno},
            </if>
            <if test="bedNo != null">#{bedNo},
            </if>
            <if test="drcode != null">#{drcode},
            </if>
            <if test="drname != null">#{drname},
            </if>
            <if test="nurseId != null">#{nurseId},
            </if>
            <if test="nurseName != null">#{nurseName},
            </if>
            <if test="checkFlag != null">#{checkFlag},
            </if>
            <if test="longTaskReason != null">#{longTaskReason},
            </if>
            <if test="opid != null">#{opid},
            </if>
            <if test="opcode != null">#{opcode},
            </if>
            <if test="opdesc != null">#{opdesc},
            </if>
            <if test="oplevelcode != null">#{oplevelcode},
            </if>
            <if test="opleveldesc != null">#{opleveldesc},
            </if>
        </trim>
    </insert>
    <update id="updatePatMedOperation" parameterType="com.ruoyi.othervisit.domain.PatMedOperation">
        update pat_med_operation
        <trim prefix="SET" suffixOverrides=",">
            <if test="patid != null">patid =
                #{patid},
            </if>
            <if test="patcode != null">patcode =
                #{patcode},
            </if>
            <if test="paname != null">paname =
                #{paname},
            </if>
            <if test="opreqid != null">opreqid =
                #{opreqid},
            </if>
            <if test="opreqcode != null">opreqcode =
                #{opreqcode},
            </if>
            <if test="opsstatus != null">opsstatus =
                #{opsstatus},
            </if>
            <if test="optypecode != null">optypecode =
                #{optypecode},
            </if>
            <if test="optypename != null">optypename =
                #{optypename},
            </if>
            <if test="operatortime != null">operatortime =
                #{operatortime},
            </if>
            <if test="opreqtime != null">opreqtime =
                #{opreqtime},
            </if>
            <if test="reqdoccode != null">reqdoccode =
                #{reqdoccode},
            </if>
            <if test="reqdocname != null">reqdocname =
                #{reqdocname},
            </if>
            <if test="reqdeptcode != null">reqdeptcode =
                #{reqdeptcode},
            </if>
            <if test="reqdeptname != null">reqdeptname =
                #{reqdeptname},
            </if>
            <if test="reqloccode != null">reqloccode =
                #{reqloccode},
            </if>
            <if test="reqlocname != null">reqlocname =
                #{reqlocname},
            </if>
            <if test="remark != null">remark =
                #{remark},
            </if>
            <if test="surgeoncode != null">surgeoncode =
                #{surgeoncode},
            </if>
            <if test="surgeonname != null">surgeonname =
                #{surgeonname},
            </if>
            <if test="firasscode != null">firasscode =
                #{firasscode},
            </if>
            <if test="firassname != null">firassname =
                #{firassname},
            </if>
            <if test="secasscode != null">secasscode =
                #{secasscode},
            </if>
            <if test="secassname != null">secassname =
                #{secassname},
            </if>
            <if test="thrasscode != null">thrasscode =
                #{thrasscode},
            </if>
            <if test="thrassname != null">thrassname =
                #{thrassname},
            </if>
            <if test="canceltime != null">canceltime =
                #{canceltime},
            </if>
            <if test="cancelreason != null">cancelreason =
                #{cancelreason},
            </if>
            <if test="cancelcode != null">cancelcode =
                #{cancelcode},
            </if>
            <if test="cancelname != null">cancelname =
                #{cancelname},
            </if>
            <if test="visittype != null">visittype =
                #{visittype},
            </if>
            <if test="status != null">status =
                #{status},
            </if>
            <if test="schemetime != null">schemetime =
                #{schemetime},
            </if>
            <if test="schemestatus != null">schemestatus =
                #{schemestatus},
            </if>
            <if test="delFlag != null">del_flag =
                #{delFlag},
            </if>
            <if test="updateBy != null">update_by =
                #{updateBy},
            </if>
            <if test="updateTime != null">update_time =
                #{updateTime},
            </if>
            <if test="createBy != null">create_by =
                #{createBy},
            </if>
            <if test="createTime != null">create_time =
                #{createTime},
            </if>
            <if test="isupload != null">isupload =
                #{isupload},
            </if>
            <if test="uploadTime != null">upload_time =
                #{uploadTime},
            </if>
            <if test="orgid != null">orgid =
                #{orgid},
            </if>
            <if test="guid != null">guid =
                #{guid},
            </if>
            <if test="pguid != null">pguid =
                #{pguid},
            </if>
            <if test="pid != null">pid =
                #{pid},
            </if>
            <if test="inhospstate != null">inhospstate =
                #{inhospstate},
            </if>
            <if test="patno != null">patno =
                #{patno},
            </if>
            <if test="patname != null">patname =
                #{patname},
            </if>
            <if test="visitid != null">visitid =
                #{visitid},
            </if>
            <if test="serialnum != null">serialnum =
                #{serialnum},
            </if>
            <if test="inhospno != null">inhospno =
                #{inhospno},
            </if>
            <if test="hospitalname != null">hospitalname =
                #{hospitalname},
            </if>
            <if test="hospitalcode != null">hospitalcode =
                #{hospitalcode},
            </if>
            <if test="deptid != null">deptid =
                #{deptid},
            </if>
            <if test="deptcode != null">deptcode =
                #{deptcode},
            </if>
            <if test="deptname != null">deptname =
                #{deptname},
            </if>
            <if test="hospitaldistrictcode != null">hospitaldistrictcode =
                #{hospitaldistrictcode},
            </if>
            <if test="hospitaldistrictname != null">hospitaldistrictname =
                #{hospitaldistrictname},
            </if>
            <if test="leaveldeptcode != null">leaveldeptcode =
                #{leaveldeptcode},
            </if>
            <if test="leaveldeptname != null">leaveldeptname =
                #{leaveldeptname},
            </if>
            <if test="leavehospitaldistrictcode != null">leavehospitaldistrictcode =
                #{leavehospitaldistrictcode},
            </if>
            <if test="leavehospitaldistrictname != null">leavehospitaldistrictname =
                #{leavehospitaldistrictname},
            </if>
            <if test="roomno != null">roomno =
                #{roomno},
            </if>
            <if test="bedNo != null">bed_no =
                #{bedNo},
            </if>
            <if test="drcode != null">drcode =
                #{drcode},
            </if>
            <if test="drname != null">drname =
                #{drname},
            </if>
            <if test="nurseId != null">nurse_id =
                #{nurseId},
            </if>
            <if test="nurseName != null">nurse_name =
                #{nurseName},
            </if>
            <if test="checkFlag != null">check_flag =
                #{checkFlag},
            </if>
            <if test="longTaskReason != null">long_task_reason =
                #{longTaskReason},
            </if>
            <if test="opid != null">opid =
                #{opid},
            </if>
            <if test="opcode != null">opcode =
                #{opcode},
            </if>
            <if test="opdesc != null">opdesc =
                #{opdesc},
            </if>
            <if test="oplevelcode != null">oplevelcode =
                #{oplevelcode},
            </if>
            <if test="opleveldesc != null">opleveldesc =
                #{opleveldesc},
            </if>
        </trim>
        where id = #{id}
    </update>
    <update id="deletePatMedOperationById" parameterType="Long">
        update pat_med_operation
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id = #{id}
    </update>
    <update id="deletePatMedOperationByIds" parameterType="String">
        update pat_med_operation
        <trim prefix="SET" suffixOverrides=",">
            del_flag =1
        </trim>
        where id in
        <foreach item="id" collection="array" open="(" separator="," close=")">
            #{id}
        </foreach>
    </update>
</mapper>
ruoyi-outpatientvisit/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
<?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-outpatientvisit</artifactId>
    <description>
        é—¨æ€¥è¯Šéšè®¿æ¨¡å—:门诊随访编排(扫描未处理门诊记录 â†’ æŒ‰ç§‘室/诊断维度匹配长期随访任务 â†’ å»ºå­ä»»åŠ¡ï¼‰çš„å”¯ä¸€å®žçŽ°å¤„ã€‚
        è¿ç§»å¯¹åº” PatMedOuthospServiceImpl#dealOutpatientInfo(@ b25c3b51)。
        ä¾èµ–方向(务必保持):
          æœ¬æ¨¡å—【只依赖 ruoyi-common】,绝不依赖 smartor。
          â€”— å¦åˆ™ smartor å·²ä¾èµ–本模块,会形成循环依赖,Maven ç›´æŽ¥æž„建失败。
          è·¨æ¨¡å—能力一律走 ruoyi-common é‡Œä¸‹æ²‰çš„æŽ¥å£ï¼š
            Â· ServiceTaskMatchApi   â€”— é•¿æœŸéšè®¿ä»»åŠ¡"匹配"(科室/诊断维度)
            Â· VisitTaskCreateApi    â€”— å­ä»»åŠ¡çš„æŸ¥/停/增(含"查已发布长期任务")
          è¿™ä¸¤ä¸ªæŽ¥å£åœ¨ smartor ä¾§éƒ½æœ‰è¿‡æ¸¡å®žçŽ°ï¼Œå°†æ¥ã€Œéšè®¿ä»»åŠ¡åˆ›å»ºæ¨¡å—ã€æŽ¥ç®¡æ—¶æ¢å®žçŽ°æ–¹å³å¯ã€‚
        æ”¯æŒé€šè¿‡ sys_config å‚数表里的 outpatientvisit.enabled æ•´ä½“关闭(运行时读取,改完最多 10 ç§’生效)。
    </description>
    <dependencies>
        <!-- é€šç”¨å·¥å…·ï¼ˆåŒæ—¶ä¼ é€’带入 mybatis / swagger-annotations / commons-lang3 / jackson ç­‰ï¼‰ -->
        <dependency>
            <groupId>com.ruoyi</groupId>
            <artifactId>ruoyi-common</artifactId>
        </dependency>
        <!--
            âš ï¸ åˆ»æ„ã€ä¸ã€‘依赖 ruoyi-quartz:
            ruoyi-quartz ä¾èµ– smartor,而 smartor ä¾èµ–本模块 â€”— å¼•入它等于间接依赖 smartor,
            æ­£æ˜¯æœ¬æ¨¡å—要避免的循环。本模块不写定时任务(定时入口在 RyTask,属调用方)。
        -->
        <!-- lombok:显式声明版本,避免注解处理器缺失 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.34</version>
        </dependency>
    </dependencies>
</project>
在上述文件截断后对比
ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/OuthospVisitModule.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospDataFacade.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/api/OuthospVisitFacade.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/config/OuthospVisitProperties.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/config/OuthospVisitSwitch.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/controller/OuthospController.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/controller/OuthospProvisionalController.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthosp.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthospError.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthospProvisional.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/domain/PatMedOuthospQueryReq.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/mapper/PatMedOuthospErrorMapper.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/mapper/PatMedOuthospMapper.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/mapper/PatMedOuthospProvisionalMapper.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/service/OuthospDataService.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/service/OuthospProvisionalService.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/service/OuthospVisitTaskService.java ruoyi-outpatientvisit/src/main/java/com/ruoyi/outpatientvisit/support/OuthospPartitionMaintainer.java ruoyi-outpatientvisit/src/main/resources/mapper/outpatientvisit/PatMedOuthospErrorMapper.xml ruoyi-outpatientvisit/src/main/resources/mapper/outpatientvisit/PatMedOuthospMapper.xml ruoyi-outpatientvisit/src/main/resources/mapper/outpatientvisit/PatMedOuthospProvisionalMapper.xml ruoyi-publicity/pom.xml ruoyi-publicity/src/main/java/com/ruoyi/publicity/PublicityModule.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/api/DefaultPublicityApi.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/api/PublicityFacade.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicityAvailabilityInterceptor.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicityProperties.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicitySwitch.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/config/PublicityWebConfig.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/controller/PublicityController.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/service/PublicityService.java ruoyi-publicity/src/main/java/com/ruoyi/publicity/service/impl/PublicityServiceImpl.java ruoyi-publicity/src/test/java/com/ruoyi/publicity/api/PublicityContextCheck.java ruoyi-publicity/src/test/resources/logback-test.xml ruoyi-template/pom.xml ruoyi-template/src/main/java/com/ruoyi/template/TemplateModule.java ruoyi-template/src/main/java/com/ruoyi/template/api/TemplateFacade.java ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateAvailabilityInterceptor.java ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateProperties.java ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateSwitch.java ruoyi-template/src/main/java/com/ruoyi/template/config/TemplateWebConfig.java ruoyi-template/src/main/java/com/ruoyi/template/controller/BaseTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/BaseTagcategoryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/HeCategoryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLibraryAssortController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLibraryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLibraryTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/HeLocallibraryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/Icd10AssociationController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaExtemplateCategoryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaExtemplateController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaExtemplatescriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptAssortController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptTargetController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaScriptTargetoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetAssortController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTargetoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateAssortController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateScriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrLibaTemplateTargetoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrTaskTemplateController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrTaskTemplateScriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/IvrTaskTemplateTargetoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SchemeLocallibraryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyCategoryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyFinishController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyFinishoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyFinishtopicController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibExceptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibResultController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptCategoryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptOptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibScriptTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateCategoryController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateScriptWjxController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateTagController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateTargetoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateTargetoptionWjxController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyLibTemplateWjxController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskScriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskScriptoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskTemplateController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskTemplateScriptController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTaskTemplateTargetoptionController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/SvyTopicController.java ruoyi-template/src/main/java/com/ruoyi/template/controller/TemplateDeptController.java ruoyi-template/src/main/java/com/ruoyi/template/domain/BaseTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/BaseTagcategory.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeCategory.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeCategoryVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibrary.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryAssort.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryAssortVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLibraryVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/HeLocallibrary.java ruoyi-template/src/main/java/com/ruoyi/template/domain/Icd10Association.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaExtemplate.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaExtemplateCategory.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaExtemplatescript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptAssort.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptAssortVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptTarget.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptTargetoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaScriptVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTarget.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetAssort.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetAssortVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetTagVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTargetoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplate.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateAssort.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateAssortVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateScriptVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateTargetoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplateVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrLibaTemplatetargetAssort.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplate.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateScriptVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateTargetoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/IvrTaskTemplateVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SchemeLocallibrary.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyCategory.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyCategoryVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyFinish.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyFinishoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyFinishtopic.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibException.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibResult.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptCategory.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptCategoryVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptOption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptRes.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibScriptTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplate.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateCategory.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateCategoryVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateReq.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateScriptVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateScriptWjx.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateTag.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateTargetoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateTargetoptionWjx.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTemplateWjx.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTopicAndOptionRes.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyLibTopicdirection.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskScriptoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplate.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateScript.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateScriptVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateTargetoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTaskTemplateVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTopic.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTopicReq.java ruoyi-template/src/main/java/com/ruoyi/template/domain/SvyTopicoption.java ruoyi-template/src/main/java/com/ruoyi/template/domain/TemplateDept.java ruoyi-template/src/main/java/com/ruoyi/template/domain/TemplateDeptVO.java ruoyi-template/src/main/java/com/ruoyi/template/domain/TemplateSmsRequest.java ruoyi-template/src/main/java/com/ruoyi/template/domain/VO/HeLibraryCountVO.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/BaseTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/BaseTagcategoryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeCategoryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLibraryAssortMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLibraryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLibraryTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/HeLocallibraryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/Icd10AssociationMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaExtemplateCategoryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaExtemplateMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaExtemplatescriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptAssortMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptTargetMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaScriptTargetoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetAssortMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTargetoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateAssortMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrLibaTemplateTargetoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrTaskTemplateMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrTaskTemplateScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/IvrTaskTemplateTargetoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SchemeLocallibraryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyCategoryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyFinishMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyFinishoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyFinishtopicMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibExceptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibResultMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptCategoryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptOptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibScriptTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateCategoryMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateScriptWjxMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateTagMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateTargetoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateTargetoptionWjxMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTemplateWjxMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyLibTopicdirectionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskScriptoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskTemplateMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskTemplateScriptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTaskTemplateTargetoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTopicMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/SvyTopicoptionMapper.java ruoyi-template/src/main/java/com/ruoyi/template/mapper/TemplateDeptMapper.java ruoyi-template/src/main/java/com/ruoyi/template/service/IBaseTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IBaseTagcategoryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IHeCategoryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLibraryAssortService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLibraryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLibraryTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IHeLocallibraryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIcd10AssociationService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplateCategoryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplateService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaExtemplatescriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptAssortService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptTargetService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaScriptTargetoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetAssortService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTargetoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateAssortService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateScriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrLibaTemplateTargetoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrTaskTemplateScriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrTaskTemplateService.java ruoyi-template/src/main/java/com/ruoyi/template/service/IIvrTaskTemplateTargetoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISchemeLocallibraryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyCategoryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyFinishService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyFinishoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyFinishtopicService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibExceptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibResultService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptCategoryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptOptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibScriptTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateCategoryService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateScriptWjxService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateTagService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateTargetoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateTargetoptionWjxService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTemplateWjxService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyLibTopicdirectionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskScriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskScriptoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskTemplateScriptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskTemplateService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTaskTemplateTargetoptionService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ISvyTopicService.java ruoyi-template/src/main/java/com/ruoyi/template/service/ITemplateDeptService.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/BaseTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/BaseTagcategoryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeCategoryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLibraryAssortServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLibraryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLibraryTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/HeLocallibraryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/Icd10AssociationServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplateCategoryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplateServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaExtemplatescriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptAssortServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptTargetServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaScriptTargetoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetAssortServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTargetoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateAssortServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateScriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrLibaTemplateTargetoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrTaskTemplateScriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrTaskTemplateServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/IvrTaskTemplateTargetoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SchemeLocallibraryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyCategoryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyFinishServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyFinishoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyFinishtopicServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibExceptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibResultServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptCategoryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptOptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibScriptTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateCategoryServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateScriptWjxServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateTagServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateTargetoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateTargetoptionWjxServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTemplateWjxServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyLibTopicdirectionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskScriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskScriptoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskTemplateScriptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskTemplateServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTaskTemplateTargetoptionServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/SvyTopicServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/service/impl/TemplateDeptServiceImpl.java ruoyi-template/src/main/java/com/ruoyi/template/support/PinYinUtil.java ruoyi-template/src/main/resources/mapper/template/BaseTagMapper.xml ruoyi-template/src/main/resources/mapper/template/BaseTagcategoryMapper.xml ruoyi-template/src/main/resources/mapper/template/HeCategoryMapper.xml ruoyi-template/src/main/resources/mapper/template/HeLibraryAssortMapper.xml ruoyi-template/src/main/resources/mapper/template/HeLibraryMapper.xml ruoyi-template/src/main/resources/mapper/template/HeLibraryTagMapper.xml ruoyi-template/src/main/resources/mapper/template/HeLocallibraryMapper.xml ruoyi-template/src/main/resources/mapper/template/Icd10AssociationMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaExtemplateCategoryMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaExtemplateMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaExtemplatescriptMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptAssortMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptTagMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptTargetMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaScriptTargetoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetAssortMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetTagMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTargetoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateAssortMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateTagMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrLibaTemplateTargetoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrTaskTemplateMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrTaskTemplateScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/IvrTaskTemplateTargetoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SchemeLocallibraryMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyCategoryMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyFinishMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyFinishoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyFinishtopicMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibExceptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibResultMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibScriptOptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibScriptTagMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplageCategoryMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateScriptWjxMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateTagMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateTargetoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateTargetoptionWjxMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTemplateWjxMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTopicCategoryMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyLibTopicdirectionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTaskScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTaskScriptoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTaskTemplateMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTaskTemplateScriptMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTaskTemplateTargetoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTopicMapper.xml ruoyi-template/src/main/resources/mapper/template/SvyTopicoptionMapper.xml ruoyi-template/src/main/resources/mapper/template/TemplateDeptMapper.xml ruoyi-template/src/test/java/com/ruoyi/template/config/TemplateSwitchCheck.java