package com.smartor.service.impl;
|
|
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.Arrays;
|
import java.util.Collections;
|
import java.util.Date;
|
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):</p>
|
* <ul>
|
* <li>门诊接诊完成:按科室/诊断匹配配置后生成妇科/儿科档案;</li>
|
* <li>住院随访完成:按出院/在院科室、病区、诊断匹配配置后生成妇科/儿科档案,
|
* 生成前按“患者 + 专科 + 档案类型”判断是否已有对应档案,已有则跳过。</li>
|
* </ul>
|
*/
|
@Slf4j
|
@Service
|
public class PatSpecificArchiveGenerateServiceImpl implements IPatSpecificArchiveGenerateService {
|
|
/** 部门类型:科室 */
|
private static final String DEPT_TYPE_DEPARTMENT = "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;
|
|
@Autowired
|
private IPatMaternityArchiveService patMaternityArchiveService;
|
|
@Autowired
|
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) {
|
if (patMedOuthosp == null) {
|
return 0;
|
}
|
// 仅丽水市妇幼保健院生效,避免影响其他机构
|
if (!SpecificArchiveConstants.LISHUI_MCH_ORG_ID.equals(patMedOuthosp.getOrgid())) {
|
return 0;
|
}
|
if (StringUtils.isBlank(patMedOuthosp.getOuthospno())) {
|
log.warn("生成专科档案跳过:门诊编号为空,serialnum={}", patMedOuthosp.getSerialnum());
|
return 0;
|
}
|
|
PatArchive targetArchive = resolvePatArchive(patMedOuthosp, patArchive);
|
if (targetArchive == null && patMedOuthosp.getPatid() != null) {
|
targetArchive = patArchiveMapper.selectPatArchiveByPatid(patMedOuthosp.getPatid());
|
}
|
Long patid = targetArchive != null ? targetArchive.getId() : patMedOuthosp.getPatid();
|
if (patid == null) {
|
log.warn("生成专科档案跳过:未找到患者档案,outhospno={},patno={}",
|
patMedOuthosp.getOuthospno(), patMedOuthosp.getPatno());
|
return 0;
|
}
|
|
String campusid = StringUtils.isNotBlank(patMedOuthosp.getCampusid())
|
? patMedOuthosp.getCampusid() : SpecificArchiveConstants.DEFAULT_CAMPUS_ID;
|
List<PatSpecificArchiveGenerateConfig> configs =
|
patSpecificArchiveGenerateConfigService.selectEffectiveConfigList(patMedOuthosp.getOrgid(), campusid);
|
if (CollectionUtils.isEmpty(configs)) {
|
log.debug("生成专科档案跳过:机构未配置档案生成规则,orgid={},campusid={}",
|
patMedOuthosp.getOrgid(), campusid);
|
return 0;
|
}
|
|
// 同一条门诊记录、同一部门分类、同一档案类型只生成一次
|
Set<String> generatedKeys = new HashSet<>();
|
int generatedCount = 0;
|
for (PatSpecificArchiveGenerateConfig config : configs) {
|
if (!matchConfig(config, patMedOuthosp.getDeptcode(), null,
|
patMedOuthosp.getIcd10code(), patMedOuthosp.getDiagname())) {
|
continue;
|
}
|
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;
|
}
|
|
String generatedKey = deptCategory + "|" + archiveType;
|
if (!generatedKeys.add(generatedKey)) {
|
continue;
|
}
|
|
try {
|
if (SpecificArchiveConstants.DEPT_CATEGORY_GYNECOLOGY.equals(deptCategory)) {
|
PatMaternityArchiveDetail detail =
|
buildMaternityDetail(patMedOuthosp, targetArchive, config, campusid);
|
patMaternityArchiveService.insertOrUpdateMaternityArchive(detail);
|
generatedCount++;
|
} else if (SpecificArchiveConstants.DEPT_CATEGORY_PEDIATRICS.equals(deptCategory)) {
|
PatChildArchiveDetail detail =
|
buildChildDetail(patMedOuthosp, targetArchive, config, campusid);
|
patChildArchiveService.insertOrUpdateChildArchive(detail);
|
generatedCount++;
|
} else {
|
log.warn("生成专科档案跳过:未知部门分类,configId={},deptCategory={}",
|
config.getId(), deptCategory);
|
}
|
} catch (Exception e) {
|
log.error("生成专科档案失败,configId={},outhospno={},deptCategory={},archiveType={}",
|
config.getId(), patMedOuthosp.getOuthospno(), deptCategory, archiveType, e);
|
}
|
}
|
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, 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)) {
|
return false;
|
}
|
|
boolean deptMatched = false;
|
if (CollectionUtils.isNotEmpty(deptCodes)) {
|
if (DEPT_TYPE_DEPARTMENT.equals(config.getDeptType())) {
|
deptMatched = containsIgnoreCase(deptCodes, deptCode);
|
} else if (DEPT_TYPE_WARD.equals(config.getDeptType())) {
|
deptMatched = containsIgnoreCase(deptCodes, wardCode);
|
}
|
}
|
|
boolean diagnosisMatched = false;
|
if (CollectionUtils.isNotEmpty(icd10Codes)) {
|
diagnosisMatched = icd10Codes.stream().anyMatch(code -> {
|
if (equalsIgnoreCase(code, icd10Code)) {
|
return true;
|
}
|
// 部分 HIS 数据只有诊断名称没有诊断码,此时允许按诊断名称兜底匹配
|
return StringUtils.isBlank(icd10Code) && equalsIgnoreCase(code, diagName);
|
});
|
}
|
|
return deptMatched || diagnosisMatched;
|
}
|
|
private PatMaternityArchiveDetail buildMaternityDetail(PatMedOuthosp patMedOuthosp, PatArchive patArchive,
|
PatSpecificArchiveGenerateConfig config, String campusid) {
|
PatMaternityArchiveDetail detail = new PatMaternityArchiveDetail();
|
detail.setArchiveType(config.getArchiveType());
|
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());
|
}
|
return detail;
|
}
|
|
private PatChildArchiveDetail buildChildDetail(PatMedOuthosp patMedOuthosp, PatArchive patArchive,
|
PatSpecificArchiveGenerateConfig config, String campusid) {
|
PatChildArchiveDetail detail = new PatChildArchiveDetail();
|
detail.setArchiveType(config.getArchiveType());
|
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())
|
: patMedOuthosp.getTelcode());
|
if (patArchive != null) {
|
detail.setParentAddress(patArchive.getPlaceOfResidence());
|
}
|
// 营养性疾病档案同步诊断信息,方便列表直接展示
|
if ("1".equals(config.getArchiveType())) {
|
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;
|
}
|
|
private void fillCommonDetail(PatMaternityArchiveDetail detail, PatMedOuthosp patMedOuthosp,
|
PatArchive patArchive, String campusid) {
|
detail.setOuthospno(patMedOuthosp.getOuthospno());
|
detail.setSerialnum(patMedOuthosp.getSerialnum());
|
detail.setPatid(patArchive != null ? patArchive.getId() : patMedOuthosp.getPatid());
|
detail.setPatname(patArchive != null
|
? firstNotBlank(patArchive.getName(), patMedOuthosp.getPatname()) : patMedOuthosp.getPatname());
|
detail.setBirthdate(patArchive != null ? patArchive.getBirthdate() : null);
|
detail.setIdcardno(patArchive != null
|
? firstNotBlank(patArchive.getIdcardno(), patMedOuthosp.getIdcardno()) : patMedOuthosp.getIdcardno());
|
detail.setPhone(patArchive != null
|
? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode()) : patMedOuthosp.getTelcode());
|
detail.setSex(patArchive != null && patArchive.getSex() != null
|
? patArchive.getSex() : patMedOuthosp.getSex());
|
detail.setDiagTime(patMedOuthosp.getAdmitdate());
|
detail.setArchiveTime(new Date());
|
detail.setDoctorCode(patMedOuthosp.getDrcode());
|
detail.setDoctorName(patMedOuthosp.getDrname());
|
detail.setManagementDoctorCode(patMedOuthosp.getDrcode());
|
detail.setManagementDoctorName(patMedOuthosp.getDrname());
|
detail.setFirstDoctorCode(patMedOuthosp.getDrcode());
|
detail.setFirstDoctorName(patMedOuthosp.getDrname());
|
detail.setDeptcode(patMedOuthosp.getDeptcode());
|
detail.setDeptname(patMedOuthosp.getDeptname());
|
detail.setIcd10code(patMedOuthosp.getIcd10code());
|
detail.setDiagname(patMedOuthosp.getDiagname());
|
detail.setRegisterUser(patMedOuthosp.getDrname());
|
detail.setOrgid(patMedOuthosp.getOrgid());
|
detail.setCampusid(campusid);
|
detail.setDelFlag("0");
|
}
|
|
private void fillCommonChildDetail(PatChildArchiveDetail detail, PatMedOuthosp patMedOuthosp,
|
PatArchive patArchive, String campusid) {
|
detail.setOuthospno(patMedOuthosp.getOuthospno());
|
detail.setSerialnum(patMedOuthosp.getSerialnum());
|
detail.setPatid(patArchive != null ? patArchive.getId() : patMedOuthosp.getPatid());
|
detail.setPatname(patArchive != null
|
? firstNotBlank(patArchive.getName(), patMedOuthosp.getPatname()) : patMedOuthosp.getPatname());
|
detail.setBirthdate(patArchive != null ? patArchive.getBirthdate() : null);
|
detail.setIdcardno(patArchive != null
|
? firstNotBlank(patArchive.getIdcardno(), patMedOuthosp.getIdcardno()) : patMedOuthosp.getIdcardno());
|
detail.setPhone(patArchive != null
|
? firstNotBlank(patArchive.getTelcode(), patMedOuthosp.getTelcode()) : patMedOuthosp.getTelcode());
|
detail.setSex(patArchive != null && patArchive.getSex() != null
|
? patArchive.getSex() : patMedOuthosp.getSex());
|
detail.setDiagTime(patMedOuthosp.getAdmitdate());
|
detail.setArchiveTime(new Date());
|
detail.setDoctorCode(patMedOuthosp.getDrcode());
|
detail.setDoctorName(patMedOuthosp.getDrname());
|
detail.setManagementDoctorCode(patMedOuthosp.getDrcode());
|
detail.setManagementDoctorName(patMedOuthosp.getDrname());
|
detail.setDeptcode(patMedOuthosp.getDeptcode());
|
detail.setDeptname(patMedOuthosp.getDeptname());
|
detail.setIcd10code(patMedOuthosp.getIcd10code());
|
detail.setDiagname(patMedOuthosp.getDiagname());
|
detail.setRegisterUser(patMedOuthosp.getDrname());
|
detail.setOrgid(patMedOuthosp.getOrgid());
|
detail.setCampusid(campusid);
|
detail.setDelFlag("0");
|
}
|
|
private PatArchive resolvePatArchive(PatMedOuthosp patMedOuthosp, PatArchive patArchive) {
|
if (patArchive != null && patArchive.getId() != null) {
|
return patArchive;
|
}
|
|
PatArchive query = new PatArchive();
|
if (StringUtils.isNotBlank(patMedOuthosp.getPatno())) {
|
query.setPatientno(patMedOuthosp.getPatno());
|
} else if (StringUtils.isNotBlank(patMedOuthosp.getIdcardno())) {
|
query.setIdcardno(patMedOuthosp.getIdcardno());
|
} else {
|
return null;
|
}
|
query.setOrgid(patMedOuthosp.getOrgid());
|
|
List<PatArchive> archives = patArchiveMapper.selectPatArchiveList(query);
|
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();
|
}
|
String value = raw.trim();
|
try {
|
JSONArray array = JSON.parseArray(value);
|
if (array != null) {
|
List<String> result = new ArrayList<>();
|
for (Object item : array) {
|
if (item != null && StringUtils.isNotBlank(String.valueOf(item))) {
|
result.add(String.valueOf(item).trim());
|
}
|
}
|
return result;
|
}
|
} catch (Exception e) {
|
log.warn("解析档案生成配置集合失败,按逗号分隔处理:{}", raw);
|
}
|
return Arrays.stream(value.split(","))
|
.map(String::trim)
|
.filter(StringUtils::isNotBlank)
|
.collect(Collectors.toList());
|
}
|
|
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) {
|
if (StringUtils.isBlank(target)) {
|
return false;
|
}
|
return source.stream().anyMatch(item -> equalsIgnoreCase(item, 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;
|
}
|
}
|