package com.docus.demo.service; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.docus.demo.dto.BasicChooseDto; import com.docus.demo.dto.BasicCorrectDto; import com.docus.demo.dto.SyncBasicDataDto; import com.docus.demo.entity.CommonResult; import com.docus.demo.entity.ScanAssort; import com.docus.demo.entity.Tbasic; import com.docus.demo.entity.TbasicSub; import com.docus.demo.entity.sqlserver.CardInfo; import com.docus.demo.entity.sqlserver1.Tdiagnose; import com.docus.demo.entity.sqlserver1.Toperation; import com.docus.demo.entity.sqlserver1.Tpatientvisit; import com.docus.demo.facade.BasicDeptService; import com.docus.demo.facade.ISyncBasicDataService; import com.docus.demo.mapper.mysql.BasicMapper; import com.docus.demo.mapper.mysql.ScanAssortMapper; import com.docus.demo.mapper.sqlserver.TcardMapper; import com.docus.demo.mapper.sqlserver1.PaintVisitMapper; import com.docus.demo.utils.SnowflakeIdWorker; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.util.*; import java.util.stream.Collectors; @Slf4j @Service public class SyncBasicDataImpl implements ISyncBasicDataService { @Autowired private BasicMapper basicMapper; @Autowired private PaintVisitMapper paintVisitMapper; @Autowired private TcardMapper tcardMapper; @Autowired private ScanAssortMapper scanAssortMapper; @Autowired private BasicDeptService basicDeptService; private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd"); // private final ExecutorService executor = Executors.newFixedThreadPool(7); @Override public CommonResult syncBasicData(SyncBasicDataDto syncBasicDataDto) { LocalDate startDate = LocalDate.parse(syncBasicDataDto.getStartDate()); LocalDate endDate = LocalDate.parse(syncBasicDataDto.getEndDate()); //每页1000条数据 List tpatientvisitList; for (int current = 1; ; current++) { syncBasicDataDto.setStartDate(startDate + " 00:00:00"); syncBasicDataDto.setEndDate(startDate + " 23:59:59"); log.info(syncBasicDataDto.getStartDate() + " --- " + syncBasicDataDto.getEndDate()); //根据出院时间查询省厅数据 tpatientvisitList = paintVisitMapper.getTpatientVisitList(0, 0, syncBasicDataDto); if (tpatientvisitList.size() == 0) { break; } log.info("开始同步" + tpatientvisitList.size()); // tpatientvisitList = paintVisitMapper.getTpatientVisitList((current - 1) * limit, current*limit, syncBasicDataDto); for (Tpatientvisit e : tpatientvisitList) { if (StrUtil.isNotBlank(e.getFprn())) { e.setFprn(e.getFprn().toLowerCase(Locale.ROOT)); } } List fprnList = tpatientvisitList.stream().map(Tpatientvisit::getFprn).collect(Collectors.toList()); List ftimesList = tpatientvisitList.stream().map(Tpatientvisit::getFtimes).distinct().collect(Collectors.toList()); log.info("fprnList" + fprnList.size() + " ftimesList" + ftimesList.size()); //诊断数据 List tdiagnoses = paintVisitMapper.getTdiagnose(fprnList, ftimesList); log.info("tdiagnoses" + tdiagnoses.size()); List toperations = paintVisitMapper.getToperation(fprnList); log.info("toperations" + toperations.size()); //数据map转换 List oldBasicList = basicMapper.getOldBasicList(fprnList, syncBasicDataDto); log.info("oldBasicList" + oldBasicList.size()); List tbasicList = this.getTbasicList(oldBasicList, tpatientvisitList, tdiagnoses, toperations); log.info("tbasicList" + tbasicList.size()); List tbasicSubList = this.getTbasicSubList(tbasicList); log.info("tbasicSubList" + tbasicSubList.size()); //数据入库 basicDeptService.handleDept(tbasicList); basicMapper.insertOrUpdateByid(tbasicList); basicMapper.insertOrUpdateTbasicSub(tbasicSubList); log.info("数据库入库" + tbasicList.size() + "子表" + tbasicSubList.size()); if (startDate.compareTo(endDate) > 0) { break; } startDate = startDate.plusDays(1); } return CommonResult.success("同步成功"); } @Override public void scanBasicCorrect(List basicCorrectDtos) { SimpleDateFormat inFormat = new SimpleDateFormat("yyyyMMdd"); List afterSyncHandle = new ArrayList<>(); for (BasicCorrectDto correctDto : basicCorrectDtos) { try { String newPatientId = correctDto.getPatientId(); String ph = correctDto.getPh(); String inpatientNo = correctDto.getInpatientNo(); String inDisDateStr = correctDto.getDisDate(); Date disDate = inFormat.parse(inDisDateStr); String disDateStr = simpleDateFormat.format(disDate); List inpatientNoList = Collections.singletonList(inpatientNo); SyncBasicDataDto syncBasicDataDto = new SyncBasicDataDto(); syncBasicDataDto.setStartDate(disDateStr + " 00:00:00"); syncBasicDataDto.setEndDate(disDateStr + " 23:59:59"); syncBasicDataDto.setInpatientNoList(inpatientNoList); Tbasic oldBasic = basicMapper.getTbasicByPatientId(newPatientId); // 二次确认 if (oldBasic == null) { List oldBasicList = basicMapper.getOldBasicList(inpatientNoList, syncBasicDataDto); // 没数据,加到同步 if (CollUtil.isEmpty(oldBasicList)) { SyncBasicDataDto syncDto = new SyncBasicDataDto(); syncDto.setInpatientNoList(inpatientNoList); syncDto.setStartDate(disDateStr); syncDto.setEndDate(disDateStr); syncDto.setLimit(1); syncBasicData(syncDto); // 同步之后在处理 afterSyncHandle.add(correctDto); continue; } oldBasic = oldBasicList.get(0); } String oldBasicPatientId = oldBasic.getPatientId(); // 更新箱号和patientId,如果更换了patientId,文件表也更换patientId basicMapper.updateScanBasicCorrect(oldBasicPatientId, newPatientId, ph); if (!newPatientId.equals(oldBasicPatientId)) { scanAssortMapper.updatePatientId(oldBasicPatientId, newPatientId); } } catch (Exception ex) { log.error("扫描修正基础数据出错了!" + ex.getMessage(), ex); } } if (CollUtil.isNotEmpty(afterSyncHandle)) { scanBasicCorrect(afterSyncHandle); } } @Override public void choose() { int count = 0; List inpatientNos = basicMapper.getInpatientNos(); log.info("本次筛选病案号数量为:{}", inpatientNos.size()); for (String inpatientNo : inpatientNos) { choose(inpatientNo); count++; log.info("已经筛选病案号:{} 个", count); } log.info("本次筛选完成!"); } /** * 根据 病案号、姓名、出院日期、住院次数 判断是同一个人,进行数据筛选。 * 有盘号/箱号 优先级 +10 * 如果有现场扫描文件数据 优先级 +4。 * 如果有联众文件数据 优先级 +3。 * 文件数量更多的,判断现场和联众 现场多优先级 +2 ,联众多优先级 +1。 * 根据最后优先级选择最高的,如果有相同选择,随机挑选一条。 * 文件数据合并到选择的患者(选择的患者没有的文件来源随机挑选一个患者)。 * 未被选择的删除基础信息。 */ private void choose(String inpatientNo) { List basicList = basicMapper.getTbasicByInpatientNo(inpatientNo); if (basicList.size() == 1) { return; } Map> group = new HashMap<>(); for (Tbasic tbasic : basicList) { String groupKey = tbasic.getInpatientNo() + "_" + tbasic.getName() + "_" + DateUtil.formatDate(tbasic.getDisDate()) + "_" + tbasic.getAdmissTimes(); List groupOrDefault = group.getOrDefault(groupKey, new ArrayList<>()); groupOrDefault.add(tbasic); group.put(groupKey, groupOrDefault); } Set>> entries = group.entrySet(); for (Map.Entry> entry : entries) { List value = entry.getValue(); if (value.size() <= 1) { continue; } groupChoose(value); } } /** * 根据重复数据已经分组进行处理 */ private void groupChoose(List basicList) { if (basicList.size() <= 1) { return; } List chooseDtoList = new ArrayList<>(); for (Tbasic basic : basicList) { BasicChooseDto chooseDto = new BasicChooseDto(); chooseDto.setPatientId(basic.getPatientId()); chooseDto.setName(basic.getName()); chooseDto.setInpatientNo(basic.getInpatientNo()); chooseDto.setAdmissTimes(basic.getAdmissTimes()); chooseDto.setDisDate(DateUtil.formatDate(basic.getDisDate())); if (StrUtil.isBlank(basic.getPh())) { chooseDto.setPriority(0); } else { chooseDto.setPriority(10); } chooseDtoList.add(chooseDto); } if (judgeAndProcess(chooseDtoList)) { return; } Map>> groupByPatientGroupBySource = new HashMap<>(10); for (BasicChooseDto basicChooseDto : chooseDtoList) { List assorts = scanAssortMapper.getListByPid(basicChooseDto.getPatientId()); assorts = assorts.stream().filter(f -> { if (f.getSource() == null) { f.setSource("unknow"); } Integer del = 1; return ObjectUtil.notEqual(del, f.getIsDel()); }).collect(Collectors.toList()); if (CollUtil.isEmpty(assorts)) { continue; } Map> groupBySource = assorts.stream().collect(Collectors.groupingBy(ScanAssort::getSource)); if (groupBySource.containsKey("-1")) { basicChooseDto.setPriority(basicChooseDto.getPriority() + 4); } if (groupBySource.containsKey("lianzhong")) { basicChooseDto.setPriority(basicChooseDto.getPriority() + 3); } groupByPatientGroupBySource.put(basicChooseDto.getPatientId(), groupBySource); } if (judgeAndProcess(chooseDtoList)) { return; } int siteMaxSize = 0; int lianzhongMaxSize = 0; HashMap> siteFileCountMap = new HashMap<>(); HashMap> lianzhongFileCountMap = new HashMap<>(); for (BasicChooseDto chooseDto : chooseDtoList) { Map> groupBySource = groupByPatientGroupBySource.getOrDefault(chooseDto.getPatientId(), new HashMap<>()); List siteScan = groupBySource.getOrDefault("-1", new ArrayList<>()); int siteSize = siteScan.size(); List lianzhong = groupBySource.getOrDefault("lianzhong", new ArrayList<>()); int lianzhongSize = lianzhong.size(); List siteGroup = siteFileCountMap.getOrDefault(siteSize, new ArrayList<>()); siteGroup.add(chooseDto); List lianzhongGroup = lianzhongFileCountMap.getOrDefault(lianzhongSize, new ArrayList<>()); lianzhongGroup.add(chooseDto); siteFileCountMap.put(siteSize, siteGroup); lianzhongFileCountMap.put(lianzhongSize, lianzhongGroup); siteMaxSize = Math.max(siteMaxSize, siteSize); lianzhongMaxSize = Math.max(lianzhongMaxSize, lianzhongSize); } List siteCountChoose = siteFileCountMap.get(siteMaxSize); List lianzhongCountChoose = lianzhongFileCountMap.get(lianzhongMaxSize); for (BasicChooseDto dto : siteCountChoose) { dto.setPriority(dto.getPriority() + 2); } for (BasicChooseDto dto : lianzhongCountChoose) { dto.setPriority(dto.getPriority() + 1); } // 最后无需再判定,直接取最大优先权中的一条 processSelection(chooseDtoList, electedOneData(chooseDtoList)); } /** * 判定选择并且处理 * * @return 如果已经选择且处理 返回true */ private boolean judgeAndProcess(List chooseDtoList) { if (elected(chooseDtoList) && electedOne(chooseDtoList)) { processSelection(chooseDtoList, electedOneData(chooseDtoList)); return true; } return false; } /** * 处理选择后的逻辑, * 文件数据合并到选择的患者(选择的患者没有的文件来源随机挑选一个患者)。 * 未被选择的删除基础信息。 * * @param chooseDtoList 选择的集合 * @param finalElected 最终的选择数据 */ private void processSelection(List chooseDtoList, BasicChooseDto finalElected) { String selectPatientId = finalElected.getPatientId(); List allPatientIds = chooseDtoList.stream().map(BasicChooseDto::getPatientId).collect(Collectors.toList()); List allScanAssorts = scanAssortMapper.getListByPids(allPatientIds); allScanAssorts = allScanAssorts.stream().filter(f -> { if (f.getSource() == null) { f.setSource("unknow"); } Integer del = 1; return ObjectUtil.notEqual(del, f.getIsDel()); }).collect(Collectors.toList()); if (CollUtil.isEmpty(allScanAssorts)) { return; } Set allSources = allScanAssorts.stream().map(ScanAssort::getSource).collect(Collectors.toSet()); Set selectPatientSources = allScanAssorts.stream() .filter(e -> ObjectUtil.equals(selectPatientId, e.getPatientId())) .map(ScanAssort::getSource) .collect(Collectors.toSet()); for (String source : allSources) { if (selectPatientSources.contains(source)) { continue; } transferScanAssortBySource(allScanAssorts, selectPatientId, source); selectPatientSources.add(source); } List notSelectedPids = allPatientIds.stream().filter(e -> !e.equals(selectPatientId)).collect(Collectors.toList()); basicMapper.delTbasic(notSelectedPids); for (String notSelectedPid : notSelectedPids) { basicMapper.delTbasicSub(notSelectedPid); } } /** * 文件数据合并到选择的患者(选择的患者没有的文件来源随机挑选一个患者)。 * * @param allScanAssorts 所有的文件数据 * @param selectPatientId 选择的患者主键 * @param source 来源 */ private void transferScanAssortBySource(List allScanAssorts, String selectPatientId, String source) { // 选择该来源的其他患者中这个来源数量最多的 Map> groupByPatient = allScanAssorts.stream() .filter(e -> !ObjectUtil.equals(selectPatientId, e.getPatientId()) && ObjectUtil.equals(source, e.getSource())) .collect(Collectors.groupingBy(ScanAssort::getPatientId)); int maxSize = 0; String maxSizePatient = null; Set>> entries = groupByPatient.entrySet(); for (Map.Entry> entry : entries) { List assorts = entry.getValue(); int size = assorts.size(); if (size > maxSize) { maxSize = size; maxSizePatient = assorts.get(0).getPatientId(); } } List updateScanAssort = groupByPatient.get(maxSizePatient); List updateScanAssortIds = updateScanAssort.stream() .map(ScanAssort::getId) .collect(Collectors.toList()); scanAssortMapper.updatePatientIdById(updateScanAssortIds, selectPatientId); } /** * 是否已经有选举,判定集合中是否已经有优先级大于0的数据 */ private boolean elected(List chooseDtoList) { for (BasicChooseDto dto : chooseDtoList) { if (dto.getPriority() > 0) { return true; } } return false; } /** * 只存在一条选举,判定集合中优先级最大的只有一条 */ private boolean electedOne(List chooseDtoList) { List list = electedMaxPriorityData(chooseDtoList); return list.size() == 1; } /** * 从最大选举权的数据选择一条 */ private BasicChooseDto electedOneData(List chooseDtoList) { List list = electedMaxPriorityData(chooseDtoList); return list.get(0); } /** * 取得最大选举权的集合 */ private List electedMaxPriorityData(List chooseDtoList) { Map> groupByPriority = chooseDtoList.stream() .collect(Collectors.groupingBy(BasicChooseDto::getPriority)); int maxPriority = 0; for (BasicChooseDto chooseDto : chooseDtoList) { int priority = chooseDto.getPriority(); maxPriority = Math.max(priority, maxPriority); } return groupByPriority.get(maxPriority); } public static void main(String[] args) { LocalDate startDate = LocalDate.parse("2024-05-06"); System.out.println(startDate); } private List getTbasicSubList(List tbasicList) { return tbasicList.stream().map(m -> { TbasicSub tbasicSub = new TbasicSub(); tbasicSub.setBirthday(m.getBirthday()); tbasicSub.setPatientId(m.getPatientId()); return tbasicSub; }).collect(Collectors.toList()); } private List getTbasicList(List oldBasicList, List tpatientvisitList, List tdiagnoses, List toperations) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); // 根据病案号 住院次数 名称 做去重判断 return tpatientvisitList.stream().map(m -> { Tbasic oldBasic = oldBasicList.stream() .filter(old -> ObjectUtil.equal(m.getFprn(), old.getInpatientNo().toLowerCase(Locale.ROOT)) && ObjectUtil.equal(m.getFtimes(), old.getAdmissTimes()) && ObjectUtil.equal(m.getFname(), old.getName()) && sdf.format(m.getFcydate()).equals(sdf.format(old.getDisDate())) ) .findFirst() .orElse(null); String fprn = m.getFprn(); Integer ftimes = m.getFtimes(); Tdiagnose tdiagnose = tdiagnoses.stream() .filter(f -> ObjectUtil.equal(f.getFprn(), fprn) && ObjectUtil.equal(f.getFtimes(), ftimes) && ObjectUtil.equal(f.getFzdlx(), "1") ) .findFirst() .orElse(null); Toperation toperation = toperations.stream() .filter(f -> ObjectUtil.equal(f.getFprn(), fprn) && ObjectUtil.equal(f.getFtimes(), ftimes) && ObjectUtil.equal(f.getFpx(), 1L) ) .findFirst() .orElse(null); Tbasic tbasic = new Tbasic(); if (oldBasic != null) { tbasic.setPatientId(oldBasic.getPatientId()); } else { tbasic.setPatientId(String.valueOf(SnowflakeIdWorker.idWorker.nextId())); } tbasic.setAdmissTimes(m.getFtimes()); tbasic.setInpatientNo(m.getFprn()); tbasic.setName(m.getFname()); tbasic.setSex(m.getFsexbh()); tbasic.setSexName(m.getFsex()); if (m.getFage().contains("Y")) { tbasic.setAge(Integer.valueOf(m.getFage().split("Y")[1])); } else if (m.getFage().contains("M")) { tbasic.setAgeMonth(Integer.valueOf(m.getFage().split("M")[1])); } else if (m.getFage().contains("D")) { tbasic.setAgeDay(Integer.valueOf(m.getFage().split("D")[1])); } tbasic.setIdCard(m.getFidcard()); tbasic.setTelphone(""); tbasic.setAdmissDate(m.getFrydate()); tbasic.setAdmissDept(m.getFrytykh()); tbasic.setAdmissDeptName(m.getFrydept()); tbasic.setDisDate(m.getFcydate()); tbasic.setDisDept(m.getFcytykh()); tbasic.setDisDeptName(m.getFcydept()); tbasic.setAdmissDays(m.getFdays()); tbasic.setAttending(m.getFzzdoctbh()); tbasic.setAttendingName(m.getFzzdoct()); tbasic.setBirthday(m.getFbirthday()); if (tdiagnose != null) { tbasic.setMainDiagCode(tdiagnose.getFicdm()); tbasic.setMainDiagName(tdiagnose.getFjbname()); } if (toperation != null) { tbasic.setMainOperateCode(toperation.getFopcode()); tbasic.setMainOperateName(toperation.getFop()); } // tbasic.setIsDead(m.getFbody()); tbasic.setCreateTime(new Date()); tbasic.setSexName(m.getFsex()); tbasic.setArchiveFileStorageType(2); tbasic.setScanUploadState(0); tbasic.setIsArchive(1); tbasic.setFileSource(3); return tbasic; }).collect(Collectors.toList()); } @Override public CommonResult syncLzBasicData(SyncBasicDataDto syncBasicDataDto) { // executor.execute(()->{ //页码 int current; //每页1000条数据 int limit = syncBasicDataDto.getLimit(); List cardInfoList; for (current = 1; ; current++) { cardInfoList = tcardMapper.getCardInfo((current - 1) * limit, current * limit, syncBasicDataDto); if (null == cardInfoList || cardInfoList.size() == 0) { break; } log.info(syncBasicDataDto.getStartDate() + " " + syncBasicDataDto.getEndDate() + "开始同步" + cardInfoList.size()); for (CardInfo e : cardInfoList) { if (StrUtil.isNotBlank(e.getPatNo())) { e.setPatNo(e.getPatNo().toLowerCase(Locale.ROOT)); } if (e.getPatNum() == null) { e.setPatNum(0); } } List fprnList = cardInfoList.stream().map(CardInfo::getPatNo).collect(Collectors.toList()); //数据map转换 List oldBasicList = basicMapper.getOldBasicList(fprnList, syncBasicDataDto); log.info("获取到旧数据" + oldBasicList.size()); List tbasicList = this.getInsertTbasic(oldBasicList, cardInfoList); if (ObjectUtil.isNotEmpty(tbasicList)) { //判断数据量 如果文件数据大于五百条 需要做拆分分批次插入 int batchSize = 1000; // 拆分列表 for (int i = 0; i < tbasicList.size(); i += batchSize) { int endIndex = Math.min(i + batchSize, tbasicList.size()); List sublist = tbasicList.subList(i, endIndex); basicDeptService.handleDept(tbasicList); //数据入库 basicMapper.insertOrUpdateByid(sublist); log.info("数据入库" + sublist.size()); } } } log.info(syncBasicDataDto.getStartDate() + " " + syncBasicDataDto.getEndDate() + "结束同步"); // }); return CommonResult.success("同步成功"); } private List getInsertTbasic(List oldBasicList, List cardInfoList) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); List cardInfos = cardInfoList.stream().filter(f -> { //根据病案号 名称 住院次数 出院时间 去重 String pno = f.getPatNo(); String name = f.getPatName(); Integer pNum = f.getPatNum(); Date outDate = f.getOutDate(); Tbasic tbasic = oldBasicList.stream().filter(o -> ObjectUtil.equal(pno, o.getInpatientNo().toLowerCase(Locale.ROOT)) && ObjectUtil.equal(name, o.getName()) && ObjectUtil.equal(pNum, o.getAdmissTimes()) && ObjectUtil.equal(pNum, o.getAdmissTimes()) && sdf.format(outDate).equals(sdf.format(o.getDisDate()))) .findAny() .orElse(null); return tbasic == null; }).collect(Collectors.toList()); List tbasicList = cardInfos.stream().map(m -> { Tbasic tbasic = new Tbasic(); tbasic.setPatientId(String.valueOf(SnowflakeIdWorker.idWorker.nextId())); tbasic.setAdmissTimes(m.getPatNum()); tbasic.setInpatientNo(m.getPatNo()); tbasic.setAdmissId(m.getPatNo()); tbasic.setName(m.getPatName()); tbasic.setSex(m.getPatSex()); tbasic.setAge(m.getPatAge()); tbasic.setIdCard(m.getPatCiticard()); tbasic.setAdmissDate(m.getInDate()); tbasic.setAdmissDept(m.getInDeptCode()); tbasic.setAdmissDeptName(m.getInDeptName()); tbasic.setDisDate(m.getOutDate()); tbasic.setDisDept(m.getOutDeptCode()); tbasic.setDisDeptName(m.getOutDeptName()); tbasic.setMainDiagName(m.getIcdeName11()); tbasic.setMainDiagCode(m.getIcdeCode11()); tbasic.setMainOperateName(m.getOperName1()); tbasic.setMainOperateCode(m.getOperIcpm1()); tbasic.setBColumn5(m.getCuid()); tbasic.setBColumn8(1); tbasic.setIsArchive(1); tbasic.setArchiveFileStorageType(2); tbasic.setCreateTime(new Date()); tbasic.setFileSource(3); return tbasic; }).collect(Collectors.toList()); return tbasicList; } }