| | |
| | | # 国际化资源文件路径 |
| | | basename: i18n/messages |
| | | profiles: |
| | | active: sltd |
| | | active: nhfy |
| | | |
| | | # 文件上传 |
| | | servlet: |
| | |
| | | package com.ruoyi.common.core.service.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | |
| | | * 其它/null = 不参与 |
| | | * </pre> |
| | | */ |
| | | @Data |
| | | public class VisitTaskRuleDto implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** 规则ID */ |
| | | /** |
| | | * 规则ID |
| | | */ |
| | | private Long id; |
| | | |
| | | /** 科室规则:"1"=首要 "2"=次要 */ |
| | | /** |
| | | * 科室规则:"1"=首要 "2"=次要 |
| | | */ |
| | | private String deptRule; |
| | | |
| | | /** 病区规则(⚠️ 原实体拼写即为 {@code wradRule}) */ |
| | | /** |
| | | * 病区规则(⚠️ 原实体拼写即为 {@code wradRule}) |
| | | */ |
| | | private String wradRule; |
| | | |
| | | /** 疾病规则 */ |
| | | /** |
| | | * 疾病规则 |
| | | */ |
| | | private String diagRule; |
| | | |
| | | /** 离院科室编码 */ |
| | | /** |
| | | * 离院科室编码 |
| | | */ |
| | | private String deptcode; |
| | | |
| | | /** 离院病区编码 */ |
| | | /** |
| | | * 离院病区编码 |
| | | */ |
| | | private String wardcode; |
| | | |
| | | /** 机构ID */ |
| | | /** |
| | | * 机构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; |
| | | } |
| | | } |
| | |
| | | @Service |
| | | public class InhospSubtaskWriter { |
| | | |
| | | /** 景宁机构ID(原 L798 硬编码;已逐字核对原实现 4 处) */ |
| | | /** |
| | | * 景宁机构ID(原 L798 硬编码;已逐字核对原实现 4 处) |
| | | */ |
| | | private static final String ORG_JINGNING = "47255004333112711A1001"; |
| | | |
| | | /** 出院随访的服务类型(再入院停机分支的必要条件,原 L803) */ |
| | | /** |
| | | * 出院随访的服务类型(再入院停机分支的必要条件,原 L803) |
| | | */ |
| | | private static final String SERVICE_TYPE_DISCHARGE = "2"; |
| | | |
| | | /** 待执行状态 */ |
| | | /** |
| | | * 待执行状态 |
| | | */ |
| | | private static final long SENDSTATE_PENDING = 2L; |
| | | |
| | | /** 停机状态(原实现把 sendstate 置 4 表示不再执行) */ |
| | | /** |
| | | * 停机状态(原实现把 sendstate 置 4 表示不再执行) |
| | | */ |
| | | private static final long SENDSTATE_STOPPED = 4L; |
| | | |
| | | /** 疾病维度(type=3)专用:停旧任务后把执行方式全置失败 */ |
| | | /** |
| | | * 疾病维度(type=3)专用:停旧任务后把执行方式全置失败 |
| | | */ |
| | | private static final String PREACHFORM_STATE_FAIL = "5"; |
| | | |
| | | @Autowired |
| | |
| | | * 原实现是 {@code void},编排层用"调用了几次"当成功数 —— 那是错的 |
| | | * (任务未发布/被去重时同样是"生成了一条")。 |
| | | */ |
| | | public boolean writeInSubTask(Long taskid, Boolean check, VisitInhospRow inhosp, |
| | | VisitArchiveRow archive, Integer type, String config) { |
| | | // 原 L683-685 |
| | | public boolean writeInSubTask(Long taskid, Boolean check, VisitInhospRow inhosp, VisitArchiveRow archive, Integer type, String config) { |
| | | 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); |
| | | log.warn("【出入院随访】VisitTaskCreateApi 不可用(任务创建模块未装配),本患者不生成子任务。" + "inhospid={}, taskid={}", inhosp.getInhospid(), taskid); |
| | | return false; |
| | | } |
| | | VisitLongTaskDto serviceTask = taskApi.findPublishedLongTask(taskid); |
| | |
| | | // 患者被重复扫到时会走"停旧 + 再 insert 一条 sendstate=4"的分支, |
| | | // 每跑一轮多一条,最终同一患者几十条。 |
| | | // ------------------------------------------------------------------ |
| | | if (taskApi.existsSubtask(archive == null ? null : archive.getId(), |
| | | inhosp.getInhospid(), taskid)) { |
| | | log.info("【出入院随访】患者{}出院记录{}任务{}已有子任务,跳过重复生成。", |
| | | inhosp.getPatid(), inhosp.getInhospid(), taskid); |
| | | if (taskApi.existsSubtask(archive == null ? null : archive.getId(), inhosp.getInhospid(), taskid)) { |
| | | log.info("【出入院随访】患者{}出院记录{}任务{}已有子任务,跳过重复生成。", inhosp.getPatid(), inhosp.getInhospid(), taskid); |
| | | writeFlagByType(inhosp.getInhospid(), type, "1", null); |
| | | return true; |
| | | } |
| | | |
| | | // 原 L732-736:组装(副作用由本类实现,见 missingOutHospTime) |
| | | VisitSubtaskDraft serviceSubtask = subtaskFactory.boxedServiceSubtask( |
| | | toTaskSource(serviceTask, inhosp), inhosp, archive, config, type, |
| | | row -> onMissingOutHospTime(row)); |
| | | // serviceSubtask数据组装(副作用由本类实现,见 missingOutHospTime) |
| | | VisitSubtaskDraft serviceSubtask = subtaskFactory.boxedServiceSubtask(toTaskSource(serviceTask, inhosp), inhosp, archive, config, type, row -> onMissingOutHospTime(row)); |
| | | if (ObjectUtils.isEmpty(serviceSubtask)) { |
| | | return false; |
| | | } |
| | | |
| | | // 原 L737-753:就诊科室/病区填充 |
| | | // 就诊科室/病区填充 |
| | | if (type != null && type != 2) { |
| | | // 科室和疾病 都用 科室填充 |
| | | if ("1".equals(inhosp.getInhospstate())) { |
| | |
| | | serviceSubtask.setVisitDeptName(inhosp.getLeavehospitaldistrictname()); |
| | | } |
| | | |
| | | // 原 L754-755:记录 insertServiceSubtask 的返回状态 |
| | | // 记录 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 (Boolean.TRUE.equals(check) && (serviceSubtask.getTaskSituation() == null || serviceSubtask.getTaskSituation() != 6)) { |
| | | // 查该患者 + 该 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 表示 "科室、病区" |
| | | //type = 1或2 表示 "科室、病区" |
| | | if (type != null && (type == 1 || type == 2)) { |
| | | Date endTime = serviceSubtask.getEndtime(); |
| | | Date endTime1 = pending.getEndtime(); |
| | |
| | | } |
| | | continue; |
| | | } |
| | | // 原 L795-800:景宁 出院首次(7天)随访一定要做 |
| | | //景宁 出院首次(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:把循环到的旧子任务停掉 |
| | | if (endTime1.before(endTime) && SERVICE_TYPE_DISCHARGE.equals(pending.getServiceType())) { |
| | | // 把循环到的旧子任务停掉 |
| | | pending.setSendstate(SENDSTATE_STOPPED); |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) { |
| | | serviceSubtask.setRemark(serviceSubtask.getRemark() + ",科室/病区患者再入院"); |
| | |
| | | i = 1; |
| | | } |
| | | } else { |
| | | // 原 L814-824:相反,则把"当前的"数据停掉(置 error + sendstate=4 后 insert) |
| | | //相反,则把"当前的"数据停掉(置 error + sendstate=4 后 insert) |
| | | serviceSubtask.setResult("error"); |
| | | serviceSubtask.setSendstate(SENDSTATE_STOPPED); |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) { |
| | | serviceSubtask.setRemark(serviceSubtask.getRemark() |
| | | + ",当前患者出院时间后有待执行的出院随访长期任务"); |
| | | serviceSubtask.setRemark(serviceSubtask.getRemark() + ",当前患者出院时间后有待执行的出院随访长期任务"); |
| | | } else { |
| | | serviceSubtask.setRemark("当前患者出院时间后有待执行的出院随访长期任务"); |
| | | } |
| | |
| | | } |
| | | } |
| | | } else if (type != null && type == 3) { |
| | | // 原 L826-841:type=3 疾病 —— 无条件停旧 + 重新新增 |
| | | //type=3 疾病 —— 无条件停旧 + 重新新增 |
| | | pending.setSendstate(SENDSTATE_STOPPED); |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getRemark())) { |
| | | serviceSubtask.setRemark(serviceSubtask.getRemark() + ",疾病患者再入院"); |
| | |
| | | } |
| | | } |
| | | } else { |
| | | // 原 L844-856:没有待执行任务 |
| | | //没有待执行任务 |
| | | markPhoneEmptyIfNeeded(serviceSubtask); |
| | | serviceSubtask.setCreateBy(serviceTask.getCreateBy()); |
| | | serviceSubtask.setCreateTime(new Date()); |
| | |
| | | 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) |
| | | // 不需要校验(check=false) |
| | | markPhoneEmptyIfNeeded(serviceSubtask); |
| | | serviceSubtask.setCreateBy(serviceTask.getCreateBy()); |
| | | serviceSubtask.setCreateTime(new Date()); |
| | |
| | | addServiceSubtaskPreachform(serviceSubtask, null); |
| | | } |
| | | |
| | | // 原 L877-899:把 check_flag 改成 1(已处理)/ 生成失败置 2 |
| | | //把 check_flag 改成 1(已处理)/ 生成失败置 2 |
| | | if (i == 1) { |
| | | writeFlagByType(inhosp.getInhospid(), type, "1", null); |
| | | } else { |
| | | log.info("生成子任务失败serviceSubtask的taskid为:{},patid为:{}", |
| | | serviceSubtask.getTaskid(), serviceSubtask.getPatid()); |
| | | log.info("生成子任务失败serviceSubtask的taskid为:{},patid为:{}", serviceSubtask.getTaskid(), serviceSubtask.getPatid()); |
| | | writeFlagByType(inhosp.getInhospid(), type, "2", "生成子任务失败 " + type); |
| | | } |
| | | return i == 1; |
| | | } catch (Exception e) { |
| | | // 原 L900-912:出错回滚事务 |
| | | log.info("生成子任务报错回滚serviceSubtask的taskid为:{},patid为:{}", |
| | | serviceSubtask.getTaskid(), serviceSubtask.getPatid()); |
| | | // 出错回滚事务 |
| | | 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); |
| | |
| | | } |
| | | } |
| | | |
| | | // ========================================================================== |
| | | // 原 L691-725:任务不存在时按 type 写标志位 |
| | | // ========================================================================== |
| | | |
| | | // 任务不存在时按 type 写标志位 |
| | | private void writeMissingTaskFlag(VisitInhospRow inhosp, Integer type, String longTaskReason, Long taskid) { |
| | | if (type == null) { |
| | | return; |
| | |
| | | switch (type) { |
| | | case 0: |
| | | log.info("该患者药品宣教长期任务不存在,任务ID为:{}", taskid); |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "medcheck_flag", "2", |
| | | longTaskReason + " & 该患者药品宣教长期任务不存在,任务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); |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "deptcheck_flag", "2", longTaskReason + " & 该患者离院科室长期任务不存在,任务ID为:" + taskid); |
| | | return; |
| | | case 2: |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "wardcheck_flag", "2", |
| | | longTaskReason + " & 该患者离院病区长期任务不存在,任务ID为:" + taskid); |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "wardcheck_flag", "2", longTaskReason + " & 该患者离院病区长期任务不存在,任务ID为:" + taskid); |
| | | return; |
| | | case 3: |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "diagcheck_flag", "2", |
| | | longTaskReason + " & 该患者疾病长期任务不存在,任务ID为:" + taskid); |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "diagcheck_flag", "2", longTaskReason + " & 该患者疾病长期任务不存在,任务ID为:" + taskid); |
| | | return; |
| | | case 4: |
| | | flagWriter.setFlagWithReason(inhosp.getInhospid(), "in_deptcheck_flag", "2", |
| | | longTaskReason + " & 该患者入院科室长期任务不存在,任务ID为:" + taskid); |
| | | 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); |
| | | 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); |
| | | log.warn("【出入院随访】任务不存在且 type={} 不属于 0-5,无法判定回写哪个标志位。" + "inhospid={}, taskid={}", type, inhosp.getInhospid(), taskid); |
| | | } |
| | | } |
| | | |
| | | // ========================================================================== |
| | | // 原 L877-912:标志位回写(成功 / 失败 / 异常回滚) |
| | | // ========================================================================== |
| | | |
| | | /** |
| | | * type → 标志位回写。<b>照抄原 L881-885 与 L892-896 的分支</b>。 |
| | |
| | | flagWriter.setFlagWithRemark(inhospid, column, flagValue, remark); |
| | | } |
| | | |
| | | // ========================================================================== |
| | | // 辅助 |
| | | // ========================================================================== |
| | | |
| | | /** |
| | | * 原 L845-851 / L864-870:手机号为空时追加备注、置 sendstate=4、result=error。 |
| | | * 手机号为空时追加备注、置 sendstate=4、result=error。 |
| | | */ |
| | | private void markPhoneEmptyIfNeeded(VisitSubtaskDraft d) { |
| | | if (StringUtils.isEmpty(d.getPhone())) { |
| | |
| | | */ |
| | | 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()); |
| | | flagWriter.setFlagWithRemark(row.getInhospid(), "deptcheck_flag", "2", (row.getRemark() == null ? "" : row.getRemark()) + "& 该患者疾病出院时间为空,出院记录ID为:" + row.getInhospid()); |
| | | } |
| | | |
| | | /** |
| | |
| | | * ⚠️ {@code type / sendDay / updateBy / createBy / serviceType / preachform} 等 |
| | | * 都在 {@link VisitLongTaskDto} 里有对应字段,直接搬。 |
| | | */ |
| | | private com.ruoyi.inhospvisit.domain.row.VisitTaskRow toTaskSource(VisitLongTaskDto dto, |
| | | VisitInhospRow inhosp) { |
| | | 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()); |
| | |
| | | try { |
| | | SubtaskPreachformWriter writer = preachformWriterProvider.stream().findFirst().orElse(null); |
| | | if (writer == null) { |
| | | log.warn("【出入院随访】SubtaskPreachformWriter 未装配,跳过执行方式明细写入。subtaskId={}", |
| | | subtask.getId()); |
| | | log.warn("【出入院随访】SubtaskPreachformWriter 未装配,跳过执行方式明细写入。subtaskId={}", subtask.getId()); |
| | | return; |
| | | } |
| | | writer.addServiceSubtaskPreachform(subtask, sendstate); |
| | | } catch (Exception e) { |
| | | // 这一步失败不应影响主流程(原实现在同一个 try 里,失败会走 catch 回滚标志位; |
| | | // 但那是因为它与 insert 同属一次事务。这里拆开后按"尽力而为"处理并记录) |
| | | log.warn("【出入院随访】写执行方式明细失败,已忽略。subtaskId={},原因={}", |
| | | subtask.getId(), e.toString()); |
| | | log.warn("【出入院随访】写执行方式明细失败,已忽略。subtaskId={},原因={}", subtask.getId(), e.toString()); |
| | | } |
| | | } |
| | | |
| | |
| | | @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}") |
| | |
| | | * @param rule 已查好的规则;{@code null} = 该科室/病区未配置规则 |
| | | * @param config {@code sys_config: visit.early.day} |
| | | */ |
| | | public void addSubTaskByRule(VisitInhospRow inhosp, VisitArchiveRow archive, |
| | | VisitTaskRule rule, String config) { |
| | | public void addSubTaskByRule(VisitInhospRow inhosp, VisitArchiveRow archive, VisitTaskRule rule, String config) { |
| | | try { |
| | | List<Integer> order = resolveDimensionOrder(rule); |
| | | log.info("【基于规则】患者{}规则:deptRule={}, wradRule={}, diagRule={};维度尝试顺序={}", |
| | | inhosp.getInhospid(), |
| | | rule == null ? null : rule.getDeptRule(), |
| | | rule == null ? null : rule.getWradRule(), |
| | | rule == null ? null : rule.getDiagRule(), order); |
| | | log.info("【基于规则】患者{}规则:deptRule={}, wradRule={}, diagRule={};维度尝试顺序={}", inhosp.getInhospid(), rule == null ? null : rule.getDeptRule(), rule == null ? null : rule.getWradRule(), rule == null ? null : rule.getDiagRule(), order); |
| | | |
| | | Integer hit = null; |
| | | for (Integer type : order) { |
| | |
| | | return order; |
| | | } |
| | | |
| | | /** 追加一个候选维度;{@code xh} 站点只允许科室维度(原 {@code !active.equals("xh")} 的等价写法) */ |
| | | /** |
| | | * 追加一个候选维度;{@code xh} 站点只允许科室维度(原 {@code !active.equals("xh")} 的等价写法) |
| | | */ |
| | | private void addDimension(List<Integer> order, int type, boolean xhSite) { |
| | | if (xhSite && type != 1) { |
| | | return; |
| | |
| | | } |
| | | } |
| | | |
| | | /** 尝试一个维度,返回"是否真的生成了(或本来就已经有)一条子任务" */ |
| | | private boolean generateOneForDimension(VisitInhospRow inhosp, VisitArchiveRow archive, |
| | | int type, String config) { |
| | | /** |
| | | * 尝试一个维度,返回"是否真的生成了(或本来就已经有)一条子任务" |
| | | */ |
| | | private boolean generateOneForDimension(VisitInhospRow inhosp, VisitArchiveRow archive, int type, String config) { |
| | | switch (type) { |
| | | case 1: |
| | | return addDeptSubTaskForPatient(inhosp, archive, config) > 0; |
| | |
| | | continue; |
| | | } |
| | | if (hit == type) { |
| | | updateCheckFlag(inhosp, type, "1", composeRemark(inhosp, |
| | | "按优先级生成一条随访任务(维度=" + dimensionName(type) + ")")); |
| | | updateCheckFlag(inhosp, type, "1", composeRemark(inhosp, "按优先级生成一条随访任务(维度=" + dimensionName(type) + ")")); |
| | | } else { |
| | | updateCheckFlag(inhosp, type, "2", composeRemark(inhosp, |
| | | "已生成一条随访任务(维度=" + dimensionName(hit) + "),本维度跳过")); |
| | | updateCheckFlag(inhosp, type, "2", composeRemark(inhosp, "已生成一条随访任务(维度=" + dimensionName(hit) + "),本维度跳过")); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** 拼 reason:沿用原实现的 {@code remark + ";xxx"} 形式 */ |
| | | /** |
| | | * 拼 reason:沿用原实现的 {@code remark + ";xxx"} 形式 |
| | | */ |
| | | private String composeRemark(VisitInhospRow inhosp, String text) { |
| | | String remark = inhosp.getRemark(); |
| | | return StringUtils.isNotEmpty(remark) ? remark + ";" + text : text; |
| | |
| | | u.setWardcheckFlag(fresh == null ? null : fresh.getWardcheckFlag()); |
| | | u.setDiagcheckFlag(fresh == null ? null : fresh.getDiagcheckFlag()); |
| | | // 原实现不判 null(fresh.getLongTaskReason() + "..."),原样保留 |
| | | u.setLongTaskReason((fresh == null ? null : fresh.getLongTaskReason()) |
| | | + "该患者所在科室未配置离院长期任务;"); |
| | | u.setLongTaskReason((fresh == null ? null : fresh.getLongTaskReason()) + "该患者所在科室未配置离院长期任务;"); |
| | | updateInhosp(u); |
| | | } else { |
| | | for (VisitTaskdeptRow t : taskdepts) { |
| | |
| | | if (subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 1, config)) { |
| | | return 1; |
| | | } |
| | | log.info("【科室任务】患者{}任务{}未生成子任务,尝试下一个候选", |
| | | inhosp.getInhospid(), t.getTaskId()); |
| | | log.info("【科室任务】患者{}任务{}未生成子任务,尝试下一个候选", inhosp.getInhospid(), t.getTaskId()); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | 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); |
| | | u.setLongTaskReason(StringUtils.isNotEmpty(fresh == null ? null : fresh.getLongTaskReason()) ? fresh.getLongTaskReason() + errorreason : errorreason); |
| | | updateInhosp(u); |
| | | } else { |
| | | for (VisitTaskdeptRow t : taskdeptList) { |
| | |
| | | if (subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 2, config)) { |
| | | return 1; |
| | | } |
| | | log.info("【病区任务】患者{}任务{}未生成子任务,尝试下一个候选", |
| | | inhosp.getInhospid(), t.getTaskId()); |
| | | log.info("【病区任务】患者{}任务{}未生成子任务,尝试下一个候选", inhosp.getInhospid(), t.getTaskId()); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | int flag = 0; |
| | | for (VisitTaskdiagRow t : taskdiags) { |
| | | // 原 L1975-1983:必须科室或病区能匹配上才建 |
| | | if (StringUtils.isNotEmpty(t.getDeptCode()) |
| | | && t.getDeptCode().equals(inhosp.getLeaveldeptcode())) { |
| | | if (StringUtils.isNotEmpty(t.getDeptCode()) && t.getDeptCode().equals(inhosp.getLeaveldeptcode())) { |
| | | flag = 1; |
| | | // 一个患者只留一条:第一条真正落库的即返回 |
| | | if (subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 3, config)) { |
| | | return 1; |
| | | } |
| | | log.info("【疾病任务】患者{}任务{}未生成子任务,尝试下一个候选", |
| | | inhosp.getInhospid(), t.getTaskId()); |
| | | } else if (StringUtils.isNotEmpty(t.getWardCode()) |
| | | && t.getWardCode().equals(inhosp.getLeavehospitaldistrictcode())) { |
| | | log.info("【疾病任务】患者{}任务{}未生成子任务,尝试下一个候选", inhosp.getInhospid(), t.getTaskId()); |
| | | } else if (StringUtils.isNotEmpty(t.getWardCode()) && t.getWardCode().equals(inhosp.getLeavehospitaldistrictcode())) { |
| | | flag = 1; |
| | | if (subtaskWriter.writeInSubTask(t.getTaskId(), true, inhosp, archive, 3, config)) { |
| | | return 1; |
| | | } |
| | | log.info("【疾病任务】患者{}任务{}未生成子任务,尝试下一个候选", |
| | | inhosp.getInhospid(), t.getTaskId()); |
| | | log.info("【疾病任务】患者{}任务{}未生成子任务,尝试下一个候选", inhosp.getInhospid(), t.getTaskId()); |
| | | } |
| | | } |
| | | if (flag == 0) { |
| | |
| | | try { |
| | | VisitTaskCreateApi api = taskApi(); |
| | | if (api == null) { |
| | | log.warn("【出入院随访】VisitTaskCreateApi 不可用,跳过再入院处理。inhospid={}", |
| | | newInHosp.getInhospid()); |
| | | log.warn("【出入院随访】VisitTaskCreateApi 不可用,跳过再入院处理。inhospid={}", newInHosp.getInhospid()); |
| | | return; |
| | | } |
| | | // 原 L1324-1328:patid + sendstate=2(不设 taskid) |
| | | List<VisitPendingSubtaskDto> pendingList = |
| | | api.findPendingSubtasks(newInHosp.getPatid(), null); |
| | | List<VisitPendingSubtaskDto> pendingList = api.findPendingSubtasks(newInHosp.getPatid(), null); |
| | | if (CollectionUtils.isEmpty(pendingList)) { |
| | | return; |
| | | } |
| | |
| | | continue; |
| | | } |
| | | // 原 L1342-1350:旧子任务的出院时间在新入院时间之前 → 停掉 |
| | | if (pending.getEndtime() != null && newInHosp.getStarttime() != null |
| | | && pending.getEndtime().before(newInHosp.getStarttime())) { |
| | | if (pending.getEndtime() != null && newInHosp.getStarttime() != null && pending.getEndtime().before(newInHosp.getStarttime())) { |
| | | pending.setSendstate(4L); |
| | | pending.setIsVisitAgain(2); |
| | | pending.setRemark("患者再入院"); |
| | |
| | | } |
| | | } |
| | | |
| | | /** 景宁机构ID(原 L1338;与 InhospSubtaskWriter 里那份是同一常量,已逐字核对) */ |
| | | /** |
| | | * 景宁机构ID(原 L1338;与 InhospSubtaskWriter 里那份是同一常量,已逐字核对) |
| | | */ |
| | | private static final String ORG_JINGNING = "47255004333112711A1001"; |
| | | |
| | | // ========================================================================== |
| | |
| | | */ |
| | | public static class VisitTaskRule { |
| | | |
| | | /** 科室规则:"1"=首要 "2"=次要,其它=不参与 */ |
| | | /** |
| | | * 科室规则:"1"=首要 "2"=次要,其它=不参与 |
| | | */ |
| | | private String deptRule; |
| | | |
| | | /** 病区规则(原字段名就是拼错的 {@code wradRule},保持同名以免读错) */ |
| | | /** |
| | | * 病区规则(原字段名就是拼错的 {@code wradRule},保持同名以免读错) |
| | | */ |
| | | private String wradRule; |
| | | |
| | | /** 疾病规则 */ |
| | | /** |
| | | * 疾病规则 |
| | | */ |
| | | private String diagRule; |
| | | |
| | | public String getDeptRule() { |
| | |
| | | } |
| | | } |
| | | |
| | | /** 供测试/日志:本类是否拿到了必需的 Mapper */ |
| | | /** |
| | | * 供测试/日志:本类是否拿到了必需的 Mapper |
| | | */ |
| | | boolean hasMappers() { |
| | | return inhospMapper != null && archiveMapper != null |
| | | && taskdeptMapper != null && taskdiagMapper != null; |
| | | return inhospMapper != null && archiveMapper != null && taskdeptMapper != null && taskdiagMapper != null; |
| | | } |
| | | |
| | | /** 预留:供日志使用的时间戳(避免 IDE 报未使用 import Date) */ |
| | | /** |
| | | * 预留:供日志使用的时间戳(避免 IDE 报未使用 import Date) |
| | | */ |
| | | @SuppressWarnings("unused") |
| | | private Date now() { |
| | | return new Date(); |
| | |
| | | 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.VisitInhospUpdateDto; |
| | | import com.ruoyi.common.core.service.dto.VisitTaskRuleDto; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.core.service.dto.VisitArchiveRow; |
| | |
| | | // 原 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(); |
| | | VisitInhospUpdateDto u = new VisitInhospUpdateDto(); |
| | | u.setInhospid(row.getInhospid()); |
| | | u.setDeptcheckFlag("2"); |
| | | u.setLongTaskReason("患者基本信息为空"); |
| | |
| | | // 原 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(); |
| | | 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("基于规则处理异常"); |
| | |
| | | * @return 原实现固定返回 1 |
| | | */ |
| | | public int dealOutHospInfoBySchedule(String config) { |
| | | log.warn("【出入院随访】dealOutHospInfoBySchedule 本次未搬迁(含手术分支与任务组 builder," |
| | | + "属第二批 ruoyi-othervisit 范围),仍由 smartor 的实现承接。config={}", config); |
| | | log.warn("【出入院随访】dealOutHospInfoBySchedule 本次未搬迁(含手术分支与任务组 builder," + "属第二批 ruoyi-othervisit 范围),仍由 smartor 的实现承接。config={}", config); |
| | | return 1; |
| | | } |
| | | |
| | |
| | | // 辅助 |
| | | // ========================================================================== |
| | | |
| | | /** 原 L1703-1707:按离院科室/病区查规则(取不到端口时返回 null = 未配置规则 → 全部生成) */ |
| | | /** |
| | | * 原 L1703-1707:按离院科室/病区查规则(取不到端口时返回 null = 未配置规则 → 全部生成) |
| | | */ |
| | | private VisitTaskRuleDto findTaskRule(VisitInhospRow row) { |
| | | PublicityDataPort port = dataPort(); |
| | | if (port == null) { |
| | | log.warn("【出入院随访】PublicityDataPort 不可用,规则按「未配置」处理(全部生成)。inhospid={}", |
| | | row.getInhospid()); |
| | | log.warn("【出入院随访】PublicityDataPort 不可用,规则按「未配置」处理(全部生成)。inhospid={}", row.getInhospid()); |
| | | return null; |
| | | } |
| | | try { |
| | |
| | | } |
| | | } |
| | | |
| | | /** 把端口的规则 DTO 适配成编排器内嵌的规则视图(字段一一对应,含拼错的 {@code wradRule}) */ |
| | | /** |
| | | * 把端口的规则 DTO 适配成编排器内嵌的规则视图(字段一一对应,含拼错的 {@code wradRule}) |
| | | */ |
| | | private InhospTaskOrchestrator.VisitTaskRule toOrchestratorRule(VisitTaskRuleDto dto) { |
| | | if (dto == null) { |
| | | return null; |
| | |
| | | return r; |
| | | } |
| | | |
| | | private void updateInhosp(com.ruoyi.common.core.service.dto.VisitInhospUpdateDto u) { |
| | | private void updateInhosp(VisitInhospUpdateDto u) { |
| | | PublicityDataPort port = dataPort(); |
| | | if (port == null) { |
| | | log.warn("【出入院随访】PublicityDataPort 不可用,无法写回标志位。inhospid={}", u.getInhospid()); |
| | |
| | | <artifactId>commons-collections4</artifactId> |
| | | <version>4.4</version> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>jakarta.jws</groupId> |
| | | <artifactId>jakarta.jws-api</artifactId> |
| | | <version>2.1.0</version> |
| | | <scope>compile</scope> |
| | | </dependency> |
| | | <dependency> |
| | | <groupId>jakarta.xml.ws</groupId> |
| | | <artifactId>jakarta.xml.ws-api</artifactId> |
| | | </dependency> |
| | | </dependencies> |
| | | |
| | | </project> |
| | |
| | | import com.ruoyi.task.domain.ServiceSubtaskPreachform; |
| | | import com.ruoyi.task.service.IServiceSubtaskService; |
| | | import com.ruoyi.task.domain.SendMagParam; |
| | | |
| | | /** |
| | | * 定时任务调度测试 |
| | | * |
| | |
| | | //1.获取发送人工号 |
| | | ServiceSubtaskDetailTraceVO subtaskDetailTraceVO = new ServiceSubtaskDetailTraceVO(); |
| | | subtaskDetailTraceVO.setHandleFlag("0"); |
| | | List<ServiceSubtaskDetailTrace> serviceSubtaskDetailTraces = |
| | | serviceSubtaskDetailTraceService.selectServiceSubtaskDetailTtraceList(subtaskDetailTraceVO); |
| | | List<ServiceSubtaskDetailTrace> serviceSubtaskDetailTraces = serviceSubtaskDetailTraceService.selectServiceSubtaskDetailTtraceList(subtaskDetailTraceVO); |
| | | // 获取“异常内容提醒" |
| | | SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("send.except.content"); |
| | | String exceptContent = sysConfig.getConfigValue(); |
| | |
| | | log.error("【dealHisDataMZ】SysConfig中的院区ID为空!"); |
| | | return; |
| | | } |
| | | List<Long> campusidList = Arrays.stream(sysConfigcampusids.get(0).getConfigValue().split(",")) |
| | | .map(String::trim).filter(StringUtils::isNotBlank).map(Long::valueOf).collect(Collectors.toList()); |
| | | List<Long> campusidList = Arrays.stream(sysConfigcampusids.get(0).getConfigValue().split(",")).map(String::trim).filter(StringUtils::isNotBlank).map(Long::valueOf).collect(Collectors.toList()); |
| | | log.info("【dealHisDataMZ】院区:{},单轮预算:{}分钟,每页:{}条", campusidList, budgetMinutes, pageSize); |
| | | |
| | | for (Long cid : campusidList) { |
| | |
| | | redisCache.deleteObject(lockKey); |
| | | } |
| | | } |
| | | log.info("【dealHisDataMZ】门急诊采集本轮结束,耗时:{}秒", |
| | | java.time.Duration.between(now, LocalDateTime.now()).getSeconds()); |
| | | log.info("【dealHisDataMZ】门急诊采集本轮结束,耗时:{}秒", java.time.Duration.between(now, LocalDateTime.now()).getSeconds()); |
| | | } |
| | | |
| | | /** |
| | | * 采集某个院区:先补一次性回溯日期,再做昨天的收尾采集,最后整轮刷新今天 |
| | | */ |
| | | private void collectOutpatientDaily(Long cid, String backfillParam, long pageSize, long maxPage, |
| | | LocalDateTime deadline) { |
| | | private void collectOutpatientDaily(Long cid, String backfillParam, long pageSize, long maxPage, LocalDateTime deadline) { |
| | | String cursorKey = "mzDayCursor:" + cid; |
| | | MzDayCursor cursor = loadMzDayCursor(cursorKey); |
| | | LocalDate today = LocalDate.now(); |
| | |
| | | // 1) 一次性回溯 |
| | | if (cursor.backfillDay != null) { |
| | | LocalDate day = LocalDate.parse(cursor.backfillDay); |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.BACKFILL, day, |
| | | cursor.backfillPage, pageSize, maxPage, deadline); |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.BACKFILL, day, cursor.backfillPage, pageSize, maxPage, deadline); |
| | | if (status != MzGatherStatusEnum.DONE) { |
| | | log.warn("【dealHisDataMZ】回溯日期 {} 本轮未完成({}),断点已保留,院区:{}", day, status.getDesc(), cid); |
| | | return; |
| | |
| | | |
| | | // 2) 昨天收尾采集:每天一次,兜住 23:xx 之后才写进 HIS 的记录和跨零点的数据 |
| | | if (!yesterday.toString().equals(cursor.finalDay)) { |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.FINAL, yesterday, |
| | | cursor.finalPage, pageSize, maxPage, deadline); |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.FINAL, yesterday, cursor.finalPage, pageSize, maxPage, deadline); |
| | | if (status != MzGatherStatusEnum.DONE) { |
| | | log.warn("【dealHisDataMZ】昨日收尾采集 {} 本轮未完成({}),断点已保留,院区:{}", yesterday, status.getDesc(), cid); |
| | | return; |
| | |
| | | cursor.page = 1L; |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.TODAY, today, |
| | | cursor.page, pageSize, maxPage, deadline); |
| | | MzGatherStatusEnum status = runDayPass(cid, cursorKey, cursor, MzGatherSlotEnum.TODAY, today, cursor.page, pageSize, maxPage, deadline); |
| | | if (status == MzGatherStatusEnum.DONE) { |
| | | // 整轮刷新完成,页码归位,下一轮重新从第一页整轮刷新 |
| | | cursor.page = 1L; |
| | |
| | | * @param slot 断点槽位(今天整轮刷新 / 昨日收尾 / 手工回溯),决定中断页码记到哪个字段 |
| | | * @return {@link MzGatherStatusEnum} 采集完成 / 预算用尽 / 接口连续失败提前结束 |
| | | */ |
| | | private MzGatherStatusEnum runDayPass(Long cid, String cursorKey, MzDayCursor cursor, MzGatherSlotEnum slot, |
| | | LocalDate day, long startPage, long pageSize, long maxPage, |
| | | LocalDateTime deadline) { |
| | | private MzGatherStatusEnum runDayPass(Long cid, String cursorKey, MzDayCursor cursor, MzGatherSlotEnum slot, LocalDate day, long startPage, long pageSize, long maxPage, LocalDateTime deadline) { |
| | | String startStr = day.format(MzGatherConstant.DAY_FMT); |
| | | String endStr = day.plusDays(1).format(MzGatherConstant.DAY_FMT); |
| | | |
| | |
| | | // 本轮有跳过的页时,断点回到第一个失败页,保证下一轮先把这些页补回来 |
| | | long resumePage = (skippedPages > 0 && firstFailPage > 0) ? firstFailPage : page; |
| | | saveMzDayPage(cursorKey, cursor, slot, resumePage); |
| | | log.warn("【dealHisDataMZ】本轮时间预算用尽,{} 本轮已采{}页{}条{},下次从第{}页继续,院区:{}", |
| | | day, pageCount, total, skippedLog(skippedPages), resumePage, cid); |
| | | log.warn("【dealHisDataMZ】本轮时间预算用尽,{} 本轮已采{}页{}条{},下次从第{}页继续,院区:{}", day, pageCount, total, skippedLog(skippedPages), resumePage, cid); |
| | | return MzGatherStatusEnum.BUDGET_UP; |
| | | } |
| | | reqVO.setCurrent(page); |
| | |
| | | firstFailPage = page; |
| | | } |
| | | consecutiveFail++; |
| | | log.error("【dealHisDataMZ】接口层采集失败,已记 error 日志并跳过该页,本轮继续往后采;" |
| | | + "院区:{},日期:{},第{}页,接口入参:{},异常:{}", |
| | | cid, day, page, new Gson().toJson(reqVO), lastError.getMessage(), lastError); |
| | | log.error("【dealHisDataMZ】接口层采集失败,已记 error 日志并跳过该页,本轮继续往后采;" + "院区:{},日期:{},第{}页,接口入参:{},异常:{}", cid, day, page, new Gson().toJson(reqVO), lastError.getMessage(), lastError); |
| | | if (consecutiveFail >= MzGatherConstant.MAX_CONSECUTIVE_PAGE_FAIL) { |
| | | // 连续多页接口失败,判定来未来接口整体不可用,本轮提前结束(不再空转后面几十页),断点留在第一个失败页 |
| | | log.error("【dealHisDataMZ】连续{}页接口失败,判定来未来接口不可用,本轮提前结束;院区:{},日期:{},断点保留在第{}页", |
| | | consecutiveFail, cid, day, firstFailPage); |
| | | log.error("【dealHisDataMZ】连续{}页接口失败,判定来未来接口不可用,本轮提前结束;院区:{},日期:{},断点保留在第{}页", consecutiveFail, cid, day, firstFailPage); |
| | | saveMzDayPage(cursorKey, cursor, slot, firstFailPage); |
| | | return MzGatherStatusEnum.FAILED; |
| | | } |
| | |
| | | consecutiveFail = 0; |
| | | |
| | | if (CollectionUtils.isEmpty(pageData)) { |
| | | log.info("【dealHisDataMZ】{} 第{}页无数据,采集结束,院区:{},本轮{}页共{}条{}", |
| | | day, page, cid, pageCount, total, skippedLog(skippedPages)); |
| | | log.info("【dealHisDataMZ】{} 第{}页无数据,采集结束,院区:{},本轮{}页共{}条{}", day, page, cid, pageCount, total, skippedLog(skippedPages)); |
| | | return MzGatherStatusEnum.DONE; |
| | | } |
| | | pageCount++; |
| | |
| | | } |
| | | } |
| | | |
| | | /** 按槽位保存中断页码 */ |
| | | /** |
| | | * 按槽位保存中断页码 |
| | | */ |
| | | private void saveMzDayPage(String cursorKey, MzDayCursor cursor, MzGatherSlotEnum slot, long page) { |
| | | if (slot == MzGatherSlotEnum.FINAL) { |
| | | cursor.finalPage = page; |
| | |
| | | saveMzDayCursor(cursorKey, cursor); |
| | | } |
| | | |
| | | /** 日志里提示接口层失败跳过了几页(下一轮整轮刷新会重新采到) */ |
| | | /** |
| | | * 日志里提示接口层失败跳过了几页(下一轮整轮刷新会重新采到) |
| | | */ |
| | | private String skippedLog(long skippedPages) { |
| | | return skippedPages > 0L ? ",接口失败跳过" + skippedPages + "页(下一轮整轮刷新会重新采到)" : ""; |
| | | } |
| | | |
| | | /** 读取 sys_config 里的门急诊采集参数(机构 orgid=20001001),取不到或超范围时用枚举里的默认值 */ |
| | | /** |
| | | * 读取 sys_config 里的门急诊采集参数(机构 orgid=20001001),取不到或超范围时用枚举里的默认值 |
| | | */ |
| | | private long getMzConfigLong(MzGatherParamEnum param) { |
| | | try { |
| | | SysConfig config = new SysConfig(); |
| | |
| | | if (value >= param.getMin() && value <= param.getMax()) { |
| | | return value; |
| | | } |
| | | log.warn("【dealHisDataMZ】配置{}的值{}超出范围[{},{}],使用默认值{}", |
| | | param.getKey(), value, param.getMin(), param.getMax(), param.getDefaultValue()); |
| | | log.warn("【dealHisDataMZ】配置{}的值{}超出范围[{},{}],使用默认值{}", param.getKey(), value, param.getMin(), param.getMax(), param.getDefaultValue()); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("【dealHisDataMZ】读取配置{}失败,使用默认值{}", param.getKey(), param.getDefaultValue(), e); |
| | |
| | | return param.getDefaultValue(); |
| | | } |
| | | |
| | | /** 解析任务入参(要一次性回溯的日期),支持 yyyy-MM-dd HH:mm:ss 与 yyyy-MM-dd */ |
| | | /** |
| | | * 解析任务入参(要一次性回溯的日期),支持 yyyy-MM-dd HH:mm:ss 与 yyyy-MM-dd |
| | | */ |
| | | private LocalDateTime parseMzStartTime(String text) { |
| | | LocalDateTime time = parseMzTimeQuiet(text); |
| | | if (time == null && StringUtils.isNotBlank(text)) { |
| | |
| | | return time; |
| | | } |
| | | |
| | | /** 静默解析日期,支持 yyyy-MM-dd HH:mm:ss 与 yyyy-MM-dd,格式不符返回 null(不刷日志) */ |
| | | /** |
| | | * 静默解析日期,支持 yyyy-MM-dd HH:mm:ss 与 yyyy-MM-dd,格式不符返回 null(不刷日志) |
| | | */ |
| | | private LocalDateTime parseMzTimeQuiet(String text) { |
| | | if (StringUtils.isBlank(text)) { |
| | | return null; |
| | |
| | | } |
| | | } |
| | | |
| | | /** 保存门急诊采集断点(各轮次中断的页码) */ |
| | | /** |
| | | * 保存门急诊采集断点(各轮次中断的页码) |
| | | */ |
| | | private void saveMzDayCursor(String cursorKey, MzDayCursor cursor) { |
| | | JSONObject json = new JSONObject(); |
| | | json.put("day", cursor.day); |
| | |
| | | redisCache.setCacheObject(cursorKey, json.toJSONString(), MzGatherConstant.CACHE_TTL_HOURS, TimeUnit.HOURS); |
| | | } |
| | | |
| | | /** 读取门急诊采集断点,读不到或解析失败时返回空断点(等价于从第一页开始整轮刷新) */ |
| | | /** |
| | | * 读取门急诊采集断点,读不到或解析失败时返回空断点(等价于从第一页开始整轮刷新) |
| | | */ |
| | | private MzDayCursor loadMzDayCursor(String cursorKey) { |
| | | MzDayCursor cursor = new MzDayCursor(); |
| | | try { |
| | |
| | | * (页码为 1 表示该轮次下一轮从第一页整轮开始) |
| | | */ |
| | | private static class MzDayCursor { |
| | | /** 今天的整轮刷新 */ |
| | | /** |
| | | * 今天的整轮刷新 |
| | | */ |
| | | private String day; |
| | | private long page = 1L; |
| | | /** 昨天的收尾采集 */ |
| | | /** |
| | | * 昨天的收尾采集 |
| | | */ |
| | | private String finalDay; |
| | | private long finalPage = 1L; |
| | | /** 待做的一次性回溯 */ |
| | | /** |
| | | * 待做的一次性回溯 |
| | | */ |
| | | private String backfillDay; |
| | | private long backfillPage = 1L; |
| | | /** 已经补过的回溯日期,避免定时任务每轮重复补同一天 */ |
| | | /** |
| | | * 已经补过的回溯日期,避免定时任务每轮重复补同一天 |
| | | */ |
| | | private String backfillDone; |
| | | } |
| | | |
| | |
| | | //2判断一下,当前的sort是不是等于需要执行的总个数,如果等于的话,说明是最后一个,直接将servuce_subtask的状态改成5,执行失败就行了 |
| | | Long cs = currentSort.get(); |
| | | |
| | | if (cs.equals(serviceSubtaskPreachforms.size()) |
| | | && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (cs.equals(serviceSubtaskPreachforms.size()) && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | serviceSubtask.setSendstate(7L); |
| | | serviceSubtask.setRemark("处理补偿任务,当前处理最后补偿,全部执行失败(超时)"); |
| | | |
| | |
| | | serviceSubtaskPreachform.setSubid(serviceSubtask.getId()); |
| | | //获取发送方式 |
| | | String sendPreachform = getSendPreachform(serviceSubtaskPreachform, serviceSubtask); |
| | | log.info("sendPreachform的发送方式是啥:{}", sendPreachform); |
| | | if (sendPreachform.equals("-1") || serviceSubtask.getSendstate() == 6 || serviceSubtask.getSendstate() == 5 || serviceSubtask.getSendstate() == 4) { |
| | | //说明已经全部遍历完了,当前的执行方式序号是最后一个了 或者 该患者已经随访结束(成功或失败了或不执行) |
| | | log.error("说明已经全部遍历完了,当前的执行方式序号是最后一个了 或者 该患者已经随访结束(成功或失败了或不执行)"); |
| | |
| | | } |
| | | //先判断患者是不是还要发送 |
| | | PatArchive patArchive = patArchiveService.selectPatArchiveByPatid(serviceSubtask.getPatid()); |
| | | log.info("patArchive的值为:{}", patArchive); |
| | | if (!Objects.isNull(patArchive) && patArchive.getNotrequiredFlag() == "1") { |
| | | //不需要发送 |
| | | serviceSubtask.setResult("患者不需要随访"); |
| | |
| | | // 在开关恢复后永远丢失。保持 sendstate 不动,开关打开后下一次 longTaskSend/compensateTask |
| | | // 会照常把它捞出来发出。 |
| | | if ("3".equals(serviceSubtask.getType()) && !publicityFacade.isEnabled()) { |
| | | log.warn("【宣教】模块未启用(参数配置 publicity.enabled=false),跳过宣教发送。subtaskId={}, patid={}", |
| | | serviceSubtask.getId(), serviceSubtask.getPatid()); |
| | | log.warn("【宣教】模块未启用(参数配置 publicity.enabled=false),跳过宣教发送。subtaskId={}, patid={}", serviceSubtask.getId(), serviceSubtask.getPatid()); |
| | | return; |
| | | } |
| | | |
| | |
| | | String url = ""; |
| | | String format = String.format("%03X", serviceOutPath.getId()); |
| | | //先判断一下发的是不是宣教 |
| | | log.info("serviceSubtask.getType的值为:{}", serviceSubtask.getType()); |
| | | if (!"3".equals(serviceSubtask.getType())) { |
| | | sendMagParam.setPhone(serviceSubtask.getPhone()); |
| | | url = StringUtils.isNotEmpty(req_path) ? localIP + ":" + req_path + "/wt?p=" + format : localIP + "/wt?p=" + format; |
| | |
| | | } else { |
| | | sysConfig = sysConfigMapper.checkConfigKeyUnique("wj.sms.template"); |
| | | } |
| | | log.info("sysConfig的值为:{}", sysConfig); |
| | | if (!Objects.isNull(sysConfig)) { |
| | | String smsContemt = sysConfig.getConfigValue(); |
| | | // sendMagParam.setContent("您好,邀请您填写出院调查表,请点击" + sendMagParam.getUrl() + "填写。感谢您配合!"); |
| | |
| | | //已收口到 ruoyi-message 模块,这里只负责组装入参并路由 |
| | | log.info("active的值为:{}", active); |
| | | SendResult smsSendResult = sendSmsByActive(serviceSubtask, sendMagParam); |
| | | log.info("smsSendResult的值为:{}", smsSendResult); |
| | | String isSuccess = smsSendResult.asLegacyFlag(); |
| | | //任务发送记录 |
| | | ServiceSubtaskRecord serviceSubtaskRecord = new ServiceSubtaskRecord(); |
| | |
| | | serviceSubtaskRecordService.insertServiceSubtaskRecord(serviceSubtaskRecord); |
| | | |
| | | //发送完成后,修改状态 |
| | | log.info("serviceSubtaskRecord的值为:{}", serviceSubtaskRecord); |
| | | serviceSubtask.setSendstate(3L); |
| | | if (StringUtils.isNotEmpty(isSuccess) && isSuccess.equals("true")) { |
| | | serviceSubtask.setResult("success"); |
| | |
| | | return "-1"; |
| | | } |
| | | // 当sort等于列表size时,并且所有sendstate都不等于9,则返回-1 |
| | | if (currentSort.get().equals((long) serviceSubtaskPreachforms.size()) |
| | | && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (currentSort.get().equals((long) serviceSubtaskPreachforms.size()) && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (allSendStateNot1AndNot9) { |
| | | //说明全发送失败了 |
| | | serviceSubtask.setSendstate(7L); |
| | |
| | | } |
| | | |
| | | public Boolean setSuccessPreachForm(ServiceSubtask serviceSubtask, String preachform, String remark, String failSendstate) { |
| | | log.info("setSuccessPreachForm的入参为:serviceSubtask = {}, preachform = {}, remark = {}, failSendstate = {}", serviceSubtask, preachform, remark, failSendstate); |
| | | if (preachform.equals("-1")) { |
| | | //说明都不需要执行了 |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform(); |
| | |
| | | serviceSubtaskPreachform.setTaskid(serviceSubtask.getTaskid()); |
| | | serviceSubtaskPreachform.setOrgid(serviceSubtask.getOrgid()); |
| | | List<ServiceSubtaskPreachform> sspCount = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform); |
| | | |
| | | log.info("sspCount的值为:{}", sspCount); |
| | | serviceSubtaskPreachform.setPreachform(preachform); |
| | | //1:未发送状态 |
| | | serviceSubtaskPreachform.setSendstate("1"); |
| | |
| | | // //人工的未发送状态,需要查2 |
| | | // if (preachform.equals("1") && StringUtils.isNotEmpty(failSendstate) && Long.valueOf(failSendstate) > 2) |
| | | // serviceSubtaskPreachform.setSendstate("2"); |
| | | log.error("-----serviceSubtaskPreachform的入参是?{}", serviceSubtaskPreachform); |
| | | log.info("-----serviceSubtaskPreachform的入参是?{}", serviceSubtaskPreachform); |
| | | List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = serviceSubtaskPreachformMapper.selectServiceSubtaskPreachformList(serviceSubtaskPreachform); |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) { |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform1 = serviceSubtaskPreachforms.get(0); |
| | |
| | | } |
| | | serviceSubtaskPreachform1.setRemark(remark); |
| | | serviceSubtaskPreachformMapper.updateServiceSubtaskPreachform(serviceSubtaskPreachform1); |
| | | |
| | | log.info("---------serviceSubtaskPreachform1的入参是 : {}", serviceSubtaskPreachform1); |
| | | //如果当前的preachform已经是最后一个了,那直接将serviceSubtask的sendstate状态改成7就行了(全超时了) |
| | | if (serviceSubtaskPreachform1.getSort() == sspCount.size() |
| | | && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (serviceSubtaskPreachform1.getSort() == sspCount.size() && isSubtaskDeadline(serviceSubtask, serviceSubtaskPreachforms)) { |
| | | if (failSendstate.equals("4") || failSendstate.equals("5")) { |
| | | serviceSubtask.setCurrentPreachform(preachform); |
| | | serviceSubtask.setSendstate(7L); |
| | |
| | | log.error("【定时任务中该患者没有查询到属于他的发送方式,subid:{},prechform:{},orgid:{}】", serviceSubtask.getId(), preachform, serviceSubtask.getOrgid()); |
| | | return false; |
| | | } |
| | | |
| | | log.info("到这里了不????"); |
| | | //将下一次的随访时间与当前执行方式记录一下 |
| | | serviceSubtask.setVisitTime(getNextVisitTime(serviceSubtask.getId(), serviceSubtask.getTaskid(), serviceSubtask.getVisitTime())); |
| | | serviceSubtask.setCurrentPreachform(preachform); |
| | |
| | | } |
| | | } |
| | | serviceSubtask.setRemark(StringUtils.isNotEmpty(serviceSubtask.getRemark()) ? serviceSubtask.getRemark() + "," + remark : remark); |
| | | log.info("serviceSubtask的入参是:{}",serviceSubtask); |
| | | serviceSubtaskMapper.updateServiceSubtask(serviceSubtask); |
| | | return true; |
| | | } |
| | |
| | | |
| | | /** |
| | | * 随访任务是否超时 |
| | | * |
| | | * @param serviceSubtask |
| | | * @param serviceSubtaskPreachforms |
| | | * @return |
| | |
| | | public boolean isSubtaskDeadline(ServiceSubtask serviceSubtask, List<ServiceSubtaskPreachform> serviceSubtaskPreachforms){ |
| | | if (ObjectUtils.isNotEmpty(serviceSubtask)) { |
| | | //只有出院随访和专病随访有超时 |
| | | if(StringUtils.isNotEmpty(serviceSubtask.getServiceType()) && |
| | | !serviceSubtask.getServiceType().equals("2") && !serviceSubtask.getServiceType().equals("13")){ |
| | | if (StringUtils.isNotEmpty(serviceSubtask.getServiceType()) && !serviceSubtask.getServiceType().equals("2") && !serviceSubtask.getServiceType().equals("13")) { |
| | | return true; |
| | | } |
| | | } |
| | |
| | | ServiceTask serviceTask = serviceTaskMapper.selectServiceTaskByTaskid(serviceSubtask.getTaskid()); |
| | | int sendDay = serviceTask != null && serviceTask.getSendDay() != null ? serviceTask.getSendDay().intValue() : 1; |
| | | Date deadline = DateUtils.addDays(serviceSubtask.getEndtime(), sendDay); |
| | | int sumCompensateTime = CollectionUtils.isEmpty(serviceSubtaskPreachforms) ? 0 : serviceSubtaskPreachforms.stream() |
| | | .mapToInt(x -> x.getCompensateTime() == null ? 0 : Integer.parseInt(x.getCompensateTime())) |
| | | .sum(); |
| | | int sumCompensateTime = CollectionUtils.isEmpty(serviceSubtaskPreachforms) ? 0 : serviceSubtaskPreachforms.stream().mapToInt(x -> x.getCompensateTime() == null ? 0 : Integer.parseInt(x.getCompensateTime())).sum(); |
| | | sumCompensateTime = sumCompensateTime + 1; |
| | | // 只补偿最后一步自身的补偿时间(天),脏值一律按 0 兜底 |
| | | deadline = DateUtils.addDays(deadline, sumCompensateTime); |
| | |
| | | serviceSubtaskVO.setVisitCount(1); |
| | | serviceSubtaskVO.setTaskid(serviceTask.getTaskid()); |
| | | serviceSubtaskVO.setEndOutHospTime(deadline); |
| | | List<ServiceSubtask> serviceSubtaskList |
| | | = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO); |
| | | List<ServiceSubtask> serviceSubtaskList = serviceSubtaskMapper.selectServiceSubtaskList(serviceSubtaskVO); |
| | | log.info("【dealSubtaskDeadline】获取到{}个超时任务, taskid={}", serviceSubtaskList.size(), serviceTask.getTaskid()); |
| | | for (ServiceSubtask serviceSubtask : serviceSubtaskList) { |
| | | PatMedInhosp patMedInhospVo = new PatMedInhosp(); |
| | |
| | | Integer type = 1; |
| | | |
| | | //新增再次随访任务 |
| | | ServiceSubtask serviceSubtaskAgain = iPatMedInhospService.boxedServiceSubtask(nextServiceTask, |
| | | patMedInhosp, patArchive, config, type); |
| | | ServiceSubtask serviceSubtaskAgain = iPatMedInhospService.boxedServiceSubtask(nextServiceTask, patMedInhosp, patArchive, config, type); |
| | | serviceSubtaskAgain.setId(null); |
| | | serviceSubtaskAgain.setUpid(serviceSubtask.getId().intValue()); |
| | | serviceSubtaskAgain.setVisitCount(serviceSubtask.getVisitCount() + 1); |
| | |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | import com.ruoyi.task.domain.entity.ServiceSubtaskEntity; |
| | | import com.ruoyi.task.domain.ServiceSubtask; |
| | | import com.ruoyi.task.domain.ServiceTask; |
| | |
| | | // 见 OuthospSubtaskDraft 的类注释。 |
| | | // ------------------------------------------------------------------ |
| | | if (d instanceof com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) { |
| | | com.ruoyi.common.core.service.dto.OuthospSubtaskDraft o = |
| | | (com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) d; |
| | | com.ruoyi.common.core.service.dto.OuthospSubtaskDraft o = (com.ruoyi.common.core.service.dto.OuthospSubtaskDraft) d; |
| | | // hospno 已上提到父类 VisitSubtaskDraft(上面统一映射过了),这里不再重复 |
| | | // 只有父类没有的字段才在这里补映射(其余如 sendname/sfzh/phone/sex/age 父类已有) |
| | | e.setAdmindate(o.getAdmitdate()); |
| | |
| | | } |
| | | } |
| | | |
| | | if (CollectionUtils.isNotEmpty(selectServiceSubtaskList)) { |
| | | //将当前方式的发送状态改成已领取 |
| | | ServiceSubtask serviceSubtask = selectServiceSubtaskList.get(0); |
| | | ServiceSubtaskPreachform serviceSubtaskPreachform = new ServiceSubtaskPreachform(); |
| | | serviceSubtaskPreachform.setPreachform(serviceSubtask.getCurrentPreachform()); |
| | | serviceSubtaskPreachform.setSubid(serviceSubtask.getId()); |
| | | serviceSubtaskPreachform.setTaskid(serviceTask.getTaskid()); |
| | | List<ServiceSubtaskPreachform> serviceSubtaskPreachforms = taskPreachformPort.selectServiceSubtaskPreachformList(serviceSubtaskPreachform); |
| | | if (CollectionUtils.isNotEmpty(serviceSubtaskPreachforms)) { |
| | | ServiceSubtaskPreachform ssp = serviceSubtaskPreachforms.get(0); |
| | | //人工无-3已领取状态(service_subtask_preachform.sendstate = 3) |
| | | if(StringUtils.isNotEmpty(ssp.getPreachform()) && !ssp.getPreachform().equals("1")){ |
| | | ssp.setSendstate("3"); |
| | | taskPreachformPort.updateServiceSubtaskPreachform(ssp); |
| | | } |
| | | } |
| | | } |
| | | if (serviceTask != null) { |
| | | if (StringUtils.isNotEmpty(serviceTask.getKcb())) map.put("kcb", serviceTask.getKcb()); |
| | | map.put("script", info); |