| | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.ruoyi.common.core.domain.entity.PatArchive; |
| | | import com.ruoyi.common.core.redis.RedisCache; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.inhospvisit.api.InhospVisitFacade; |
| | | import com.ruoyi.inhospvisit.domain.PatMedInhosp; |
| | | import com.ruoyi.outpatientvisit.domain.PatMedOuthosp; |
| | | import com.ruoyi.patarchive.domain.PatSpecificArchiveGenerateConfig; |
| | | import com.ruoyi.patarchive.mapper.PatArchiveMapper; |
| | | import com.ruoyi.patarchive.service.IPatSpecificArchiveGenerateConfigService; |
| | | import com.ruoyi.task.api.TaskSubtaskApi; |
| | | import com.ruoyi.task.domain.ServiceSubtask; |
| | | import com.ruoyi.task.service.IServiceSubtaskService; |
| | | import com.smartor.common.SpecificArchiveConstants; |
| | | import com.smartor.domain.PatChildArchive; |
| | | import com.smartor.domain.PatChildArchiveDetail; |
| | | import com.smartor.domain.PatMaternityArchive; |
| | | import com.smartor.domain.PatMaternityArchiveDetail; |
| | | import com.smartor.domain.PatSpecificArchiveGenerateLog; |
| | | import com.smartor.service.IPatChildArchiveService; |
| | | import com.smartor.service.IPatMaternityArchiveService; |
| | | import com.smartor.service.IPatSpecificArchiveGenerateLogService; |
| | | import com.smartor.service.IPatSpecificArchiveGenerateService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | 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.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.util.concurrent.TimeUnit; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 专科档案生成Service业务层处理。 |
| | | * |
| | | * <p>只处理丽水市妇幼保健院(orgid = 47226079133110211G1001)的门诊接诊完成记录, |
| | | * 按 {@code pat_specific_archive_generate_config} 配置匹配科室和诊断后, |
| | | * 生成对应类型的妇科档案或儿科档案。</p> |
| | | * <p>只处理丽水市妇幼保健院(orgid = 47226079133110211G1001):</p> |
| | | * <ul> |
| | | * <li>门诊接诊完成:按科室/诊断匹配配置后生成妇科/儿科档案;</li> |
| | | * <li>住院随访完成:按出院/在院科室、病区、诊断匹配配置后生成妇科/儿科档案, |
| | | * 生成前按“患者 + 专科 + 档案类型”判断是否已有对应档案,已有则跳过。</li> |
| | | * </ul> |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class PatSpecificArchiveGenerateServiceImpl implements IPatSpecificArchiveGenerateService { |
| | | |
| | | /** 部门分类:妇科 */ |
| | | private static final String DEPT_CATEGORY_GYNECOLOGY = "1"; |
| | | /** 部门分类:儿科 */ |
| | | private static final String DEPT_CATEGORY_PEDIATRICS = "2"; |
| | | /** 部门类型:科室 */ |
| | | private static final String DEPT_TYPE_DEPARTMENT = "1"; |
| | | /** 来源类型:门诊记录 */ |
| | | private static final String SOURCE_TYPE_OUTHOSP = "1"; |
| | | /** 部门类型:病区 */ |
| | | private static final String DEPT_TYPE_WARD = "2"; |
| | | /** 就诊类型:出院 */ |
| | | private static final String HOSP_TYPE_DISCHARGE = "2"; |
| | | /** 就诊类型:住院/入院 */ |
| | | private static final String HOSP_TYPE_INHOSP = "6"; |
| | | /** 住院随访完成状态 */ |
| | | private static final Long SEND_STATE_FINISHED = 6L; |
| | | /** 住院随访档案生成的最大重试次数 */ |
| | | private static final int MAX_RETRY = 3; |
| | | /** Redis 锁前缀 */ |
| | | private static final String LOCK_PREFIX = "lock:archive:inhosp:"; |
| | | |
| | | @Autowired |
| | | private IPatSpecificArchiveGenerateConfigService patSpecificArchiveGenerateConfigService; |
| | |
| | | private IPatChildArchiveService patChildArchiveService; |
| | | |
| | | @Autowired |
| | | private IPatSpecificArchiveGenerateLogService patSpecificArchiveGenerateLogService; |
| | | |
| | | @Autowired |
| | | private PatArchiveMapper patArchiveMapper; |
| | | |
| | | @Autowired |
| | | private IServiceSubtaskService serviceSubtaskService; |
| | | |
| | | @Autowired |
| | | private InhospVisitFacade inhospVisitFacade; |
| | | |
| | | @Autowired |
| | | private TaskSubtaskApi taskSubtaskApi; |
| | | |
| | | @Autowired |
| | | private RedisCache redisCache; |
| | | |
| | | /** |
| | | * 住院随访服务类型白名单,默认出院随访、专科随访、入院随访、手术随访。 |
| | | */ |
| | | @Value("${archive.inhosp.followup.service-types:2,13,18,19}") |
| | | private String inhospFollowupServiceTypes; |
| | | |
| | | /** 住院随访生成专科档案开关,默认开启,便于按机构灰度。 */ |
| | | @Value("${archive.inhosp.followup.enabled:true}") |
| | | private boolean inhospFollowupEnabled; |
| | | |
| | | @Override |
| | | public int generateByOuthosp(PatMedOuthosp patMedOuthosp, PatArchive patArchive) { |
| | |
| | | } |
| | | |
| | | String campusid = StringUtils.isNotBlank(patMedOuthosp.getCampusid()) |
| | | ? patMedOuthosp.getCampusid() : "1"; |
| | | ? patMedOuthosp.getCampusid() : SpecificArchiveConstants.DEFAULT_CAMPUS_ID; |
| | | List<PatSpecificArchiveGenerateConfig> configs = |
| | | patSpecificArchiveGenerateConfigService.selectEffectiveConfigList(patMedOuthosp.getOrgid(), campusid); |
| | | if (CollectionUtils.isEmpty(configs)) { |
| | |
| | | Set<String> generatedKeys = new HashSet<>(); |
| | | int generatedCount = 0; |
| | | for (PatSpecificArchiveGenerateConfig config : configs) { |
| | | if (!matchConfig(config, patMedOuthosp)) { |
| | | if (!matchConfig(config, patMedOuthosp.getDeptcode(), null, |
| | | patMedOuthosp.getIcd10code(), patMedOuthosp.getDiagname())) { |
| | | continue; |
| | | } |
| | | String archiveType = config.getArchiveType(); |
| | |
| | | } |
| | | |
| | | try { |
| | | if (DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) { |
| | | if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) { |
| | | PatMaternityArchiveDetail detail = |
| | | buildMaternityDetail(patMedOuthosp, targetArchive, config, campusid); |
| | | patMaternityArchiveService.insertOrUpdateMaternityArchive(detail); |
| | | generatedCount++; |
| | | } else if (DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) { |
| | | } else if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) { |
| | | PatChildArchiveDetail detail = |
| | | buildChildDetail(patMedOuthosp, targetArchive, config, campusid); |
| | | patChildArchiveService.insertOrUpdateChildArchive(detail); |
| | |
| | | return generatedCount; |
| | | } |
| | | |
| | | @Override |
| | | public int generateByInhospFollowup(Long subId) { |
| | | if (!inhospFollowupEnabled || subId == null) { |
| | | return 0; |
| | | } |
| | | ServiceSubtask subtask = serviceSubtaskService.selectServiceSubtaskById(subId); |
| | | if (subtask == null || !SEND_STATE_FINISHED.equals(subtask.getSendstate())) { |
| | | return 0; |
| | | } |
| | | if (!isInhospFollowup(subtask)) { |
| | | return 0; |
| | | } |
| | | |
| | | // 仅限丽水妇幼,机构为空时用住院记录/患者档案兜底 |
| | | PatMedInhosp inhosp = resolveInhosp(subtask); |
| | | PatArchive patArchive = resolvePatArchive(subtask.getPatid(), inhosp); |
| | | String orgid = firstNotBlank(subtask.getOrgid(), |
| | | inhosp == null ? null : inhosp.getOrgid(), |
| | | patArchive == null ? null : patArchive.getOrgid()); |
| | | if (!SpecificArchiveConstants.LISHUI_MCH_ORG_ID.equals(orgid)) { |
| | | log.debug("住院随访生成专科档案跳过:非丽水妇幼机构,subId={},orgid={}", subId, orgid); |
| | | return 0; |
| | | } |
| | | |
| | | Long patid = firstNonNull(subtask.getPatid(), |
| | | inhosp == null ? null : inhosp.getPatid(), |
| | | patArchive == null ? null : patArchive.getId()); |
| | | if (patid == null) { |
| | | log.warn("住院随访生成专科档案跳过:未找到患者,subId={},inhospid={}", subId, subtask.getInhospid()); |
| | | return 0; |
| | | } |
| | | if (patArchive == null) { |
| | | patArchive = patArchiveMapper.selectPatArchiveByPatid(patid); |
| | | } |
| | | |
| | | String campusid = firstNotBlank(subtask.getCampusid(), |
| | | inhosp == null ? null : inhosp.getCampusid(), |
| | | patArchive == null ? null : patArchive.getCampusid(), |
| | | SpecificArchiveConstants.DEFAULT_CAMPUS_ID); |
| | | |
| | | boolean admission = HOSP_TYPE_INHOSP.equals(subtask.getHospType()); |
| | | String deptCode = admission |
| | | ? firstNotBlank(inhosp == null ? null : inhosp.getDeptcode(), subtask.getDeptcode(), |
| | | subtask.getVisitDeptCode(), subtask.getLeaveldeptcode()) |
| | | : firstNotBlank(inhosp == null ? null : inhosp.getLeaveldeptcode(), subtask.getLeaveldeptcode(), |
| | | subtask.getVisitDeptCode(), inhosp == null ? null : inhosp.getDeptcode()); |
| | | String deptName = admission |
| | | ? firstNotBlank(inhosp == null ? null : inhosp.getDeptname(), subtask.getDeptname(), |
| | | subtask.getVisitDeptName(), subtask.getLeaveldeptname()) |
| | | : firstNotBlank(inhosp == null ? null : inhosp.getLeaveldeptname(), subtask.getLeaveldeptname(), |
| | | subtask.getVisitDeptName(), inhosp == null ? null : inhosp.getDeptname()); |
| | | String wardCode = admission |
| | | ? firstNotBlank(inhosp == null ? null : inhosp.getHospitaldistrictcode(), |
| | | subtask.getHospitaldistrictcode(), subtask.getLeavehospitaldistrictcode()) |
| | | : firstNotBlank(inhosp == null ? null : inhosp.getLeavehospitaldistrictcode(), |
| | | subtask.getLeavehospitaldistrictcode(), subtask.getHospitaldistrictcode()); |
| | | String wardName = admission |
| | | ? firstNotBlank(inhosp == null ? null : inhosp.getHospitaldistrictname(), |
| | | subtask.getHospitaldistrictname(), subtask.getLeavehospitaldistrictname()) |
| | | : firstNotBlank(inhosp == null ? null : inhosp.getLeavehospitaldistrictname(), |
| | | subtask.getLeavehospitaldistrictname(), subtask.getHospitaldistrictname()); |
| | | String icd10Code = admission |
| | | ? firstNotBlank(inhosp == null ? null : inhosp.getIcd10code(), subtask.getLeaveicd10code()) |
| | | : firstNotBlank(inhosp == null ? null : inhosp.getLeaveicd10code(), |
| | | subtask.getLeaveicd10code(), inhosp == null ? null : inhosp.getIcd10code()); |
| | | String diagName = admission |
| | | ? firstNotBlank(inhosp == null ? null : inhosp.getDiagname(), subtask.getDiagname()) |
| | | : firstNotBlank(inhosp == null ? null : inhosp.getLeavediagname(), |
| | | subtask.getLeavediagname(), inhosp == null ? null : inhosp.getDiagname()); |
| | | |
| | | InhospArchiveContext context = new InhospArchiveContext(); |
| | | context.subtask = subtask; |
| | | context.inhosp = inhosp; |
| | | context.patArchive = patArchive; |
| | | context.patid = patid; |
| | | context.orgid = orgid; |
| | | context.campusid = campusid; |
| | | context.deptCode = deptCode; |
| | | context.deptName = deptName; |
| | | context.wardCode = wardCode; |
| | | context.wardName = wardName; |
| | | context.icd10Code = icd10Code; |
| | | context.diagName = diagName; |
| | | context.diagTime = firstDate(subtask.getFinishtime(), inhosp == null ? null : inhosp.getEndtime()); |
| | | |
| | | List<PatSpecificArchiveGenerateConfig> configs = |
| | | patSpecificArchiveGenerateConfigService.selectEffectiveConfigList(orgid, campusid); |
| | | if (CollectionUtils.isEmpty(configs)) { |
| | | recordLog(context, null, null, null, SpecificArchiveConstants.RESULT_NO_MATCH, null, |
| | | "机构未配置档案生成规则"); |
| | | return 0; |
| | | } |
| | | |
| | | Set<String> generatedKeys = new HashSet<>(); |
| | | int generatedCount = 0; |
| | | boolean anyMatched = false; |
| | | for (PatSpecificArchiveGenerateConfig config : configs) { |
| | | String archiveType = config.getArchiveType(); |
| | | String deptCategory = config.getDeptCategory(); |
| | | if (StringUtils.isBlank(archiveType) || StringUtils.isBlank(deptCategory)) { |
| | | log.warn("住院随访生成专科档案跳过:配置不完整,configId={},deptCategory={},archiveType={}", |
| | | config.getId(), deptCategory, archiveType); |
| | | continue; |
| | | } |
| | | if (!matchConfig(config, deptCode, wardCode, icd10Code, diagName)) { |
| | | continue; |
| | | } |
| | | anyMatched = true; |
| | | String generatedKey = deptCategory + "|" + archiveType; |
| | | if (!generatedKeys.add(generatedKey)) { |
| | | continue; |
| | | } |
| | | if (alreadyProcessed(subId, config.getId())) { |
| | | continue; |
| | | } |
| | | |
| | | String lockKey = LOCK_PREFIX + orgid + ":" + patid + ":" + deptCategory + ":" + archiveType; |
| | | boolean locked = false; |
| | | try { |
| | | locked = redisCache.setCacheObjectIfAbsent(lockKey, "1", 30, TimeUnit.SECONDS); |
| | | } catch (Exception e) { |
| | | // Redis 不可用时 fail-open,依赖生成日志和数据库查询兜底,避免档案生成整体停摆 |
| | | locked = true; |
| | | log.warn("住院随访生成专科档案获取Redis锁异常,降级为无锁执行,subId={},lockKey={}", subId, lockKey, e); |
| | | } |
| | | if (!locked) { |
| | | // 未拿到锁时由补偿任务重试,避免并发重复生成 |
| | | log.info("住院随访生成专科档案未获取到锁,等待补偿重试,subId={},lockKey={}", subId, lockKey); |
| | | recordLog(context, config, deptCategory, archiveType, SpecificArchiveConstants.RESULT_FAILED, |
| | | null, "获取生成锁失败"); |
| | | continue; |
| | | } |
| | | |
| | | try { |
| | | Long existingArchiveId = findExistingArchiveId(orgid, patid, deptCategory, archiveType); |
| | | if (existingArchiveId != null) { |
| | | recordLog(context, config, deptCategory, archiveType, |
| | | SpecificArchiveConstants.RESULT_EXISTS, existingArchiveId, "患者已有对应档案,跳过生成"); |
| | | continue; |
| | | } |
| | | |
| | | Long archiveId; |
| | | if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) { |
| | | PatMaternityArchiveDetail detail = buildInhospMaternityDetail(context, config); |
| | | archiveId = (long) patMaternityArchiveService.insertOrUpdateMaternityArchive(detail); |
| | | } else if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) { |
| | | PatChildArchiveDetail detail = buildInhospChildDetail(context, config); |
| | | archiveId = (long) patChildArchiveService.insertOrUpdateChildArchive(detail); |
| | | } else { |
| | | log.warn("住院随访生成专科档案跳过:未知部门分类,configId={},deptCategory={}", |
| | | config.getId(), deptCategory); |
| | | continue; |
| | | } |
| | | recordLog(context, config, deptCategory, archiveType, |
| | | SpecificArchiveConstants.RESULT_GENERATED, archiveId, "生成成功"); |
| | | generatedCount++; |
| | | } catch (Exception e) { |
| | | log.error("住院随访生成专科档案失败,subId={},configId={},deptCategory={},archiveType={}", |
| | | subId, config.getId(), deptCategory, archiveType, e); |
| | | recordLog(context, config, deptCategory, archiveType, |
| | | SpecificArchiveConstants.RESULT_FAILED, null, truncateMsg(e.getMessage())); |
| | | } finally { |
| | | if (locked) { |
| | | try { |
| | | redisCache.deleteObject(lockKey); |
| | | } catch (Exception e) { |
| | | log.warn("住院随访生成专科档案释放Redis锁异常,lockKey={}", lockKey, e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (!anyMatched) { |
| | | recordLog(context, null, null, null, SpecificArchiveConstants.RESULT_NO_MATCH, null, |
| | | "未命中科室/病区/诊断配置"); |
| | | } |
| | | return generatedCount; |
| | | } |
| | | |
| | | @Override |
| | | public int compensateInhospFollowups() { |
| | | if (!inhospFollowupEnabled) { |
| | | return 0; |
| | | } |
| | | List<String> serviceTypes = parseCsv(inhospFollowupServiceTypes); |
| | | if (CollectionUtils.isEmpty(serviceTypes)) { |
| | | return 0; |
| | | } |
| | | Date startTime = new Date(System.currentTimeMillis() - 7L * 24 * 60 * 60 * 1000); |
| | | List<ServiceSubtask> subtasks = taskSubtaskApi.findCompletedInhospFollowups( |
| | | SpecificArchiveConstants.LISHUI_MCH_ORG_ID, serviceTypes, startTime, 200); |
| | | if (CollectionUtils.isEmpty(subtasks)) { |
| | | return 0; |
| | | } |
| | | int generated = 0; |
| | | for (ServiceSubtask subtask : subtasks) { |
| | | if (subtask.getId() == null |
| | | || patSpecificArchiveGenerateLogService.existsProcessedBySourceSub( |
| | | SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP, subtask.getId())) { |
| | | continue; |
| | | } |
| | | try { |
| | | generated += generateByInhospFollowup(subtask.getId()); |
| | | } catch (Exception e) { |
| | | log.error("补偿生成住院随访专科档案失败,subId={}", subtask.getId(), e); |
| | | } |
| | | } |
| | | return generated; |
| | | } |
| | | |
| | | /** |
| | | * 判断门诊记录是否命中配置。 |
| | | * 判断随访子任务是否属于需要生成档案的住院随访。 |
| | | */ |
| | | private boolean isInhospFollowup(ServiceSubtask subtask) { |
| | | if (!HOSP_TYPE_DISCHARGE.equals(subtask.getHospType()) |
| | | && !HOSP_TYPE_INHOSP.equals(subtask.getHospType())) { |
| | | return false; |
| | | } |
| | | if (StringUtils.isBlank(subtask.getServiceType())) { |
| | | return false; |
| | | } |
| | | return parseCsv(inhospFollowupServiceTypes).contains(subtask.getServiceType().trim()); |
| | | } |
| | | |
| | | private boolean alreadyProcessed(Long subId, Long configId) { |
| | | int cid = configId == null ? SpecificArchiveConstants.NO_CONFIG_ID : configId.intValue(); |
| | | PatSpecificArchiveGenerateLog old = patSpecificArchiveGenerateLogService |
| | | .selectBySourceSubAndConfig(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP, subId, cid); |
| | | if (old == null) { |
| | | return false; |
| | | } |
| | | if (SpecificArchiveConstants.RESULT_FAILED.equals(old.getProcessResult())) { |
| | | return old.getRetryCount() != null && old.getRetryCount() >= MAX_RETRY; |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 查询患者是否已有对应专科档案。 |
| | | * |
| | | * <p>用户确认口径:按“专科 + 档案类型”判断。妇科、儿科分表存储,因此妇科查妇科表, |
| | | * 儿科查儿科表,再按档案类型区分。</p> |
| | | */ |
| | | private Long findExistingArchiveId(String orgid, Long patid, String deptCategory, String archiveType) { |
| | | if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) { |
| | | PatMaternityArchive query = new PatMaternityArchive(); |
| | | query.setOrgid(orgid); |
| | | query.setPatid(patid); |
| | | query.setArchiveType(archiveType); |
| | | List<PatMaternityArchive> list = patMaternityArchiveService.selectPatMaternityArchiveList(query); |
| | | return CollectionUtils.isNotEmpty(list) ? list.get(0).getId() : null; |
| | | } |
| | | if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) { |
| | | PatChildArchive query = new PatChildArchive(); |
| | | query.setOrgid(orgid); |
| | | query.setPatid(patid); |
| | | query.setArchiveType(archiveType); |
| | | List<PatChildArchive> list = patChildArchiveService.selectPatChildArchiveList(query); |
| | | return CollectionUtils.isNotEmpty(list) ? list.get(0).getId() : null; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * 判断科室/病区、诊断是否命中配置。 |
| | | * |
| | | * <p>部门条件与诊断条件为 OR 关系:任一条件命中即生成;两个集合都为空时配置无效。</p> |
| | | */ |
| | | private boolean matchConfig(PatSpecificArchiveGenerateConfig config, PatMedOuthosp patMedOuthosp) { |
| | | private boolean matchConfig(PatSpecificArchiveGenerateConfig config, String deptCode, String wardCode, |
| | | String icd10Code, String diagName) { |
| | | List<String> deptCodes = parseCodes(config.getDeptCodes()); |
| | | List<String> icd10Codes = parseCodes(config.getIcd10Codes()); |
| | | if (CollectionUtils.isEmpty(deptCodes) && CollectionUtils.isEmpty(icd10Codes)) { |
| | |
| | | |
| | | boolean deptMatched = false; |
| | | if (CollectionUtils.isNotEmpty(deptCodes)) { |
| | | // 门诊记录只有科室,没有病区;dept_type=2(病区)不参与门诊匹配 |
| | | if (DEPT_TYPE_DEPARTMENT.equals(config.getDeptType())) { |
| | | deptMatched = containsIgnoreCase(deptCodes, patMedOuthosp.getDeptcode()); |
| | | deptMatched = containsIgnoreCase(deptCodes, deptCode); |
| | | } else if (DEPT_TYPE_WARD.equals(config.getDeptType())) { |
| | | deptMatched = containsIgnoreCase(deptCodes, wardCode); |
| | | } |
| | | } |
| | | |
| | | boolean diagnosisMatched = false; |
| | | if (CollectionUtils.isNotEmpty(icd10Codes)) { |
| | | String icd10code = patMedOuthosp.getIcd10code(); |
| | | String diagname = patMedOuthosp.getDiagname(); |
| | | diagnosisMatched = icd10Codes.stream().anyMatch(code -> { |
| | | if (equalsIgnoreCase(code, icd10code)) { |
| | | if (equalsIgnoreCase(code, icd10Code)) { |
| | | return true; |
| | | } |
| | | // 部分 HIS 数据只有诊断名称没有诊断码,此时允许按诊断名称兜底匹配 |
| | | return StringUtils.isBlank(icd10code) && equalsIgnoreCase(code, diagname); |
| | | return StringUtils.isBlank(icd10Code) && equalsIgnoreCase(code, diagName); |
| | | }); |
| | | } |
| | | |
| | |
| | | PatSpecificArchiveGenerateConfig config, String campusid) { |
| | | PatMaternityArchiveDetail detail = new PatMaternityArchiveDetail(); |
| | | detail.setArchiveType(config.getArchiveType()); |
| | | detail.setSourceType(SOURCE_TYPE_OUTHOSP); |
| | | detail.setFollowupPeriod(config.getFollowupPeriod()); |
| | | detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_OUTHOSP); |
| | | detail.setSourceBizId(patMedOuthosp.getId()); |
| | | detail.setSourceBizNo(patMedOuthosp.getOuthospno()); |
| | | fillCommonDetail(detail, patMedOuthosp, patArchive, campusid); |
| | | if (patArchive != null) { |
| | | detail.setAddress(patArchive.getPlaceOfResidence()); |
| | |
| | | PatSpecificArchiveGenerateConfig config, String campusid) { |
| | | PatChildArchiveDetail detail = new PatChildArchiveDetail(); |
| | | detail.setArchiveType(config.getArchiveType()); |
| | | detail.setSourceType(SOURCE_TYPE_OUTHOSP); |
| | | detail.setFollowupPeriod(config.getFollowupPeriod()); |
| | | detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_OUTHOSP); |
| | | detail.setSourceBizId(patMedOuthosp.getId()); |
| | | detail.setSourceBizNo(patMedOuthosp.getOuthospno()); |
| | | fillCommonChildDetail(detail, patMedOuthosp, patArchive, campusid); |
| | | detail.setParentPhone(patArchive != null |
| | | ? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode()) |
| | |
| | | detail.setNutritionIcd10Code(patMedOuthosp.getIcd10code()); |
| | | detail.setNutritionDiagName(patMedOuthosp.getDiagname()); |
| | | } |
| | | return detail; |
| | | } |
| | | |
| | | private PatMaternityArchiveDetail buildInhospMaternityDetail(InhospArchiveContext context, |
| | | PatSpecificArchiveGenerateConfig config) { |
| | | PatMaternityArchiveDetail detail = new PatMaternityArchiveDetail(); |
| | | detail.setArchiveType(config.getArchiveType()); |
| | | detail.setFollowupPeriod(config.getFollowupPeriod()); |
| | | detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP); |
| | | detail.setSourceBizId(inInhospid(context)); |
| | | detail.setSourceBizNo(inInhospno(context)); |
| | | detail.setSourceSubId(context.subtask.getId()); |
| | | detail.setOuthospno(null); |
| | | detail.setInhospno(inInhospno(context)); |
| | | detail.setSerialnum(inSerialnum(context)); |
| | | detail.setPatid(context.patid); |
| | | detail.setPatname(firstNotBlank(context.patArchive == null ? null : context.patArchive.getName(), |
| | | context.inhosp == null ? null : context.inhosp.getPatname())); |
| | | detail.setBirthdate(context.patArchive == null ? null : context.patArchive.getBirthdate()); |
| | | detail.setIdcardno(firstNotBlank(context.patArchive == null ? null : context.patArchive.getIdcardno(), |
| | | context.inhosp == null ? null : context.inhosp.getIdcardno())); |
| | | detail.setPhone(firstNotBlank(context.patArchive == null ? null : context.patArchive.getTelcode(), |
| | | context.inhosp == null ? null : context.inhosp.getTelcode())); |
| | | detail.setSex(firstNonNull(context.patArchive == null ? null : context.patArchive.getSex(), |
| | | context.inhosp == null ? null : context.inhosp.getSex(), |
| | | context.subtask.getSex())); |
| | | detail.setDiagTime(context.diagTime); |
| | | detail.setArchiveTime(new Date()); |
| | | detail.setDoctorCode(inDrcode(context)); |
| | | detail.setDoctorName(inDrname(context)); |
| | | detail.setManagementDoctorCode(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctorCode(), |
| | | inDrcode(context))); |
| | | detail.setManagementDoctorName(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctor(), |
| | | inDrname(context))); |
| | | detail.setFirstDoctorCode(inDrcode(context)); |
| | | detail.setFirstDoctorName(inDrname(context)); |
| | | detail.setDeptcode(context.deptCode); |
| | | detail.setDeptname(context.deptName); |
| | | detail.setWardcode(context.wardCode); |
| | | detail.setWardname(context.wardName); |
| | | detail.setIcd10code(context.icd10Code); |
| | | detail.setDiagname(context.diagName); |
| | | detail.setAddress(context.patArchive == null ? null : context.patArchive.getPlaceOfResidence()); |
| | | detail.setRegisterUser("住院随访"); |
| | | detail.setOrgid(context.orgid); |
| | | detail.setCampusid(context.campusid); |
| | | detail.setDelFlag("0"); |
| | | return detail; |
| | | } |
| | | |
| | | private PatChildArchiveDetail buildInhospChildDetail(InhospArchiveContext context, |
| | | PatSpecificArchiveGenerateConfig config) { |
| | | PatChildArchiveDetail detail = new PatChildArchiveDetail(); |
| | | detail.setArchiveType(config.getArchiveType()); |
| | | detail.setFollowupPeriod(config.getFollowupPeriod()); |
| | | detail.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP); |
| | | detail.setSourceBizId(inInhospid(context)); |
| | | detail.setSourceBizNo(inInhospno(context)); |
| | | detail.setSourceSubId(context.subtask.getId()); |
| | | detail.setOuthospno(null); |
| | | detail.setInhospno(inInhospno(context)); |
| | | detail.setSerialnum(inSerialnum(context)); |
| | | detail.setPatid(context.patid); |
| | | detail.setPatname(firstNotBlank(context.patArchive == null ? null : context.patArchive.getName(), |
| | | context.inhosp == null ? null : context.inhosp.getPatname())); |
| | | detail.setBirthdate(context.patArchive == null ? null : context.patArchive.getBirthdate()); |
| | | detail.setIdcardno(firstNotBlank(context.patArchive == null ? null : context.patArchive.getIdcardno(), |
| | | context.inhosp == null ? null : context.inhosp.getIdcardno())); |
| | | detail.setPhone(firstNotBlank(context.patArchive == null ? null : context.patArchive.getTelcode(), |
| | | context.inhosp == null ? null : context.inhosp.getTelcode())); |
| | | detail.setSex(firstNonNull(context.patArchive == null ? null : context.patArchive.getSex(), |
| | | context.inhosp == null ? null : context.inhosp.getSex(), |
| | | context.subtask.getSex())); |
| | | detail.setDiagTime(context.diagTime); |
| | | detail.setArchiveTime(new Date()); |
| | | detail.setDoctorCode(inDrcode(context)); |
| | | detail.setDoctorName(inDrname(context)); |
| | | detail.setManagementDoctorCode(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctorCode(), |
| | | inDrcode(context))); |
| | | detail.setManagementDoctorName(firstNotBlank(context.inhosp == null ? null : context.inhosp.getManagementDoctor(), |
| | | inDrname(context))); |
| | | detail.setDeptcode(context.deptCode); |
| | | detail.setDeptname(context.deptName); |
| | | detail.setWardcode(context.wardCode); |
| | | detail.setWardname(context.wardName); |
| | | detail.setIcd10code(context.icd10Code); |
| | | detail.setDiagname(context.diagName); |
| | | detail.setParentPhone(firstNotBlank(context.patArchive == null ? null : context.patArchive.getTelcode(), |
| | | context.inhosp == null ? null : context.inhosp.getTelcode())); |
| | | detail.setParentAddress(context.patArchive == null ? null : context.patArchive.getPlaceOfResidence()); |
| | | if ("1".equals(config.getArchiveType())) { |
| | | detail.setNutritionIcd10Code(context.icd10Code); |
| | | detail.setNutritionDiagName(context.diagName); |
| | | } |
| | | detail.setRegisterUser("住院随访"); |
| | | detail.setOrgid(context.orgid); |
| | | detail.setCampusid(context.campusid); |
| | | detail.setDelFlag("0"); |
| | | return detail; |
| | | } |
| | | |
| | |
| | | return CollectionUtils.isNotEmpty(archives) ? archives.get(0) : null; |
| | | } |
| | | |
| | | private PatArchive resolvePatArchive(Long patid, PatMedInhosp inhosp) { |
| | | if (patid != null) { |
| | | PatArchive archive = patArchiveMapper.selectPatArchiveByPatid(patid); |
| | | if (archive != null) { |
| | | return archive; |
| | | } |
| | | } |
| | | Long inhospPatid = inhosp == null ? null : inhosp.getPatid(); |
| | | return inhospPatid == null ? null : patArchiveMapper.selectPatArchiveByPatid(inhospPatid); |
| | | } |
| | | |
| | | private PatMedInhosp resolveInhosp(ServiceSubtask subtask) { |
| | | if (subtask.getInhospid() != null) { |
| | | PatMedInhosp inhosp = inhospVisitFacade.selectPatMedInhospByInhospid(subtask.getInhospid()); |
| | | if (inhosp != null) { |
| | | return inhosp; |
| | | } |
| | | } |
| | | if (StringUtils.isNotBlank(subtask.getHospno())) { |
| | | PatMedInhosp query = new PatMedInhosp(); |
| | | query.setInhospno(subtask.getHospno()); |
| | | query.setOrgid(subtask.getOrgid()); |
| | | List<PatMedInhosp> list = inhospVisitFacade.selectPatMedInhosp(query); |
| | | if (CollectionUtils.isNotEmpty(list)) { |
| | | return list.get(0); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void recordLog(InhospArchiveContext context, PatSpecificArchiveGenerateConfig config, |
| | | String deptCategory, String archiveType, String result, Long archiveId, String msg) { |
| | | PatSpecificArchiveGenerateLog log = new PatSpecificArchiveGenerateLog(); |
| | | log.setSourceType(SpecificArchiveConstants.SOURCE_TYPE_INHOSP_FOLLOWUP); |
| | | log.setSourceBizId(inInhospid(context)); |
| | | log.setSourceBizNo(inInhospno(context)); |
| | | log.setSourceSubId(context.subtask.getId()); |
| | | log.setPatid(context.patid); |
| | | log.setOrgid(context.orgid); |
| | | log.setCampusid(context.campusid); |
| | | log.setDeptCategory(deptCategory); |
| | | log.setArchiveType(archiveType); |
| | | log.setConfigId(config == null || config.getId() == null |
| | | ? SpecificArchiveConstants.NO_CONFIG_ID : config.getId().intValue()); |
| | | log.setArchiveId(archiveId); |
| | | log.setProcessResult(result); |
| | | log.setProcessMsg(msg); |
| | | PatSpecificArchiveGenerateLog old = patSpecificArchiveGenerateLogService.selectBySourceSubAndConfig( |
| | | log.getSourceType(), log.getSourceSubId(), log.getConfigId()); |
| | | int retry = old == null || old.getRetryCount() == null ? 0 : old.getRetryCount(); |
| | | if (SpecificArchiveConstants.RESULT_FAILED.equals(result)) { |
| | | retry++; |
| | | } |
| | | log.setRetryCount(retry); |
| | | patSpecificArchiveGenerateLogService.saveOrUpdate(log); |
| | | } |
| | | |
| | | private Long inInhospid(InhospArchiveContext context) { |
| | | return firstNonNull(context.inhosp == null ? null : context.inhosp.getInhospid(), |
| | | context.subtask.getInhospid()); |
| | | } |
| | | |
| | | private String inInhospno(InhospArchiveContext context) { |
| | | return firstNotBlank(context.inhosp == null ? null : context.inhosp.getInhospno(), |
| | | context.subtask.getHospno()); |
| | | } |
| | | |
| | | private String inSerialnum(InhospArchiveContext context) { |
| | | return context.inhosp == null ? null : context.inhosp.getSerialnum(); |
| | | } |
| | | |
| | | private String inDrcode(InhospArchiveContext context) { |
| | | return firstNotBlank(context.inhosp == null ? null : context.inhosp.getDrcode(), context.subtask.getDrcode()); |
| | | } |
| | | |
| | | private String inDrname(InhospArchiveContext context) { |
| | | return firstNotBlank(context.inhosp == null ? null : context.inhosp.getDrname(), context.subtask.getDrname()); |
| | | } |
| | | |
| | | private List<String> parseCodes(String raw) { |
| | | if (StringUtils.isBlank(raw)) { |
| | | return Collections.emptyList(); |
| | |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private String firstNotBlank(String first, String second) { |
| | | return StringUtils.isNotBlank(first) ? first : second; |
| | | private List<String> parseCsv(String raw) { |
| | | if (StringUtils.isBlank(raw)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return Arrays.stream(raw.split(",")) |
| | | .map(String::trim) |
| | | .filter(StringUtils::isNotBlank) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private String firstNotBlank(String... values) { |
| | | if (values == null) { |
| | | return null; |
| | | } |
| | | for (String value : values) { |
| | | if (StringUtils.isNotBlank(value)) { |
| | | return value; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private <T> T firstNonNull(T... values) { |
| | | if (values == null) { |
| | | return null; |
| | | } |
| | | for (T value : values) { |
| | | if (value != null) { |
| | | return value; |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Date firstDate(Date first, Date second) { |
| | | return first != null ? first : second; |
| | | } |
| | | |
| | | private boolean containsIgnoreCase(List<String> source, String target) { |
| | |
| | | private boolean equalsIgnoreCase(String first, String second) { |
| | | return first != null && second != null && first.trim().equalsIgnoreCase(second.trim()); |
| | | } |
| | | |
| | | private String truncateMsg(String msg) { |
| | | if (msg == null) { |
| | | return null; |
| | | } |
| | | return msg.length() <= 900 ? msg : msg.substring(0, 900); |
| | | } |
| | | |
| | | /** |
| | | * 住院随访生成档案时用到的上下文。 |
| | | */ |
| | | private static class InhospArchiveContext { |
| | | private ServiceSubtask subtask; |
| | | private PatMedInhosp inhosp; |
| | | private PatArchive patArchive; |
| | | private Long patid; |
| | | private String orgid; |
| | | private String campusid; |
| | | private String deptCode; |
| | | private String deptName; |
| | | private String wardCode; |
| | | private String wardName; |
| | | private String icd10Code; |
| | | private String diagName; |
| | | private Date diagTime; |
| | | } |
| | | } |