package com.smartor.service.impl; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.core.domain.entity.SysUser; import com.ruoyi.common.core.domain.entity.SysUserDept; import com.ruoyi.common.core.domain.entity.SysUserRole; import com.ruoyi.common.utils.HttpUtil; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.http.HttpUtils; import com.ruoyi.system.domain.SysConfig; import com.ruoyi.system.mapper.SysConfigMapper; import com.smartor.domain.*; import com.smartor.mapper.*; import com.smartor.service.*; 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.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Type; import java.net.URLEncoder; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.*; /** * 新华数据采集处理 * * @author smartor * @date 2023-03-04 */ @Slf4j @Service public class XHGatherPatArchiveServiceImpl implements IXHGatherPatArchiveService { @Value("${hosp_info_url}") private String hospInfoUrl; @Autowired IPatMedInhospService patMedInhospService; @Autowired IPatArchiveService patArchiveService; @Autowired IServiceAppointRecordService iServiceAppointRecordService; @Autowired private SysConfigMapper sysConfigMapper; @Value("${xhsmsPath}") private String xhsmsPath; // 帐号 @Value("${xhsmsAccount}") private String xhsmsAccount; // 接口密码 @Value("${xhsmsPwd}") private String xhsmsPwd; // 虚拟接入码 @Value("${xhsmsjrm}") private String xhsmsjrm; /** * 获取患者出入院信息 * * @param cry 出入院类型:0-入院,1-出院 * @param startTime 开始时间 * @param endTime 结束时间 */ @Transactional public void getInHospDataGather(String cry, LocalDateTime startTime, LocalDateTime endTime) { String typeName = "0".equals(cry) ? "入院" : "出院"; log.info("【getInHospInfo】开始获取{}患者信息,时间范围:{} ~ {}", typeName, startTime, endTime); long startTimeMillis = System.currentTimeMillis(); try { // 第一步:构建请求参数并调用HIS接口 List thiedInhospInfoList = fetchHisData(cry, startTime, endTime); if (CollectionUtils.isEmpty(thiedInhospInfoList)) { log.info("【getInHospInfo】{}患者信息为空,跳过处理", typeName); return; } log.info("【getInHospInfo】获取到{}条{}患者信息", thiedInhospInfoList.size(), typeName); // 第二步:保存原始数据到文件(用于备份) saveRawDataToFile(cry, thiedInhospInfoList); // 第三步:处理患者数据 processPatientData(thiedInhospInfoList, cry); long endTimeMillis = System.currentTimeMillis(); log.info("【getInHospInfo】{}患者信息处理完成,耗时:{}ms", typeName, endTimeMillis - startTimeMillis); } catch (Exception e) { log.error("【getInHospInfo】获取{}患者信息异常", typeName, e); throw new RuntimeException("获取" + typeName + "患者信息失败", e); } } /** * 获取预约停诊记录信息 * * @param cry 出入院类型:0-入院,1-出院 2,预约记录 * @param startTime 开始时间 * @param endTime 结束时间 */ public void getAppointRecordInfo(String cry, LocalDateTime startTime, LocalDateTime endTime) { log.info("【getAppointRecordInfo】开始获取取预约停诊记录信息,时间范围:{} ~ {}", startTime, endTime); long startTimeMillis = System.currentTimeMillis(); try { // 第一步:构建请求参数并调用HIS接口 List appointRecordFromHIS = getAppointRecordFromHIS(cry, startTime, endTime); if (CollectionUtils.isEmpty(appointRecordFromHIS)) { log.info("【getAppointRecordInfo】获取预约停诊记录信息为空,跳过处理"); return; } log.info("【getAppointRecordInfo】获取预约停诊记录信息{}条", appointRecordFromHIS.size()); SysConfig sysConfig = sysConfigMapper.checkConfigKeyUnique("yytz.sms.template"); if (sysConfig != null) { String smsContemt = sysConfig.getConfigValue(); // 第二步:处理停诊记录信息 for (ServiceAppointRecord serviceAppointRecord : appointRecordFromHIS) { String replace = smsContemt.replace("${patFileName}", StringUtils.defaultString(serviceAppointRecord.getPatFileName())).replace("${registTimeRange}", StringUtils.defaultString(serviceAppointRecord.getRegistTimeRange())).replace("${registDeptName}", StringUtils.defaultString(serviceAppointRecord.getRegistDeptName())).replace("${treatDoctName}", StringUtils.defaultString(serviceAppointRecord.getTreatDoctName())); Map map2 = new HashMap(); map2.put("action", "send"); map2.put("account", xhsmsAccount); map2.put("password", xhsmsPwd); map2.put("mobile", serviceAppointRecord.getPatTel()); map2.put("content", replace); map2.put("extno", xhsmsjrm); map2.put("rt", "json"); StringBuilder encodedParams = new StringBuilder(); for (Map.Entry entry : map2.entrySet()) { if (encodedParams.length() > 0) { encodedParams.append("&"); } // 对键和值进行URL编码 String encodedKey = URLEncoder.encode(entry.getKey(), "UTF-8"); String encodedValue = URLEncoder.encode(entry.getValue(), "UTF-8"); encodedParams.append(encodedKey).append("=").append(encodedValue); } Boolean isSuccess = HttpUtils.sendPost(xhsmsPath, encodedParams.toString()); if (isSuccess) { log.info("---处理停诊记录信息发送成功,入参为:{}", encodedParams.toString()); serviceAppointRecord.setSendTime(new Date()); serviceAppointRecord.setSenderName("系统管理员"); iServiceAppointRecordService.insertServiceAppointRecord(serviceAppointRecord); } else { log.info("处理停诊记录信息发送失败"); iServiceAppointRecordService.insertServiceAppointRecord(serviceAppointRecord); } } } long endTimeMillis = System.currentTimeMillis(); log.info("【getAppointRecordInfo】{},预约停诊记录信息处理完成,耗时:{}ms", endTimeMillis - startTimeMillis); } catch (Exception e) { log.error("【getAppointRecordInfo】获取预约停诊记录信息处理异常", e); throw new RuntimeException("获取预约停诊记录信息处理异常", e); } } /** * 第一步:调用HIS接口获取数据 */ private List fetchHisData(String cry, LocalDateTime startTime, LocalDateTime endTime) { try { // 构建请求参数 Map requestParams = buildRequestParams(cry, startTime, endTime); // 构建请求头 Map headers = buildRequestHeaders(startTime); // 发送HTTP请求 String result = HttpUtils.sendPostByHeader(hospInfoUrl, new Gson().toJson(requestParams), headers); // 解析响应数据 return parseResponseData(result); } catch (Exception e) { log.error("【fetchHisData】调用HIS接口异常", e); throw e; } } private List getAppointRecordFromHIS(String cry, LocalDateTime startTime, LocalDateTime endTime) { try { // 构建请求参数 Map requestParams = buildRequestParams(cry, startTime, endTime); // 构建请求头 Map headers = buildRequestHeaders(startTime); // 发送HTTP请求 log.info("【getAppointRecordFromHIS】发送HTTP请求,请求参数:{},请求地址:{}", new Gson().toJson(requestParams), hospInfoUrl); String result = HttpUtils.sendPostByHeader(hospInfoUrl, new Gson().toJson(requestParams), headers); // 解析响应数据 return parseResponseData2(result); } catch (Exception e) { log.error("【fetchHisData】调用HIS接口异常", e); throw e; } } /** * 第二步:保存原始数据到文件 */ private void saveRawDataToFile(String cry, List dataList) { try { String typeName = "0".equals(cry) ? "入院信息" : "出院信息"; String filename = typeName + "_" + System.currentTimeMillis() + ".json"; String filePath = "D:\\public\\HIS数据采集\\" + filename; File file = new File(filePath); File parentDir = file.getParentFile(); if (!parentDir.exists()) { parentDir.mkdirs(); } if (!file.exists()) { file.createNewFile(); } // 将数据写入文件 try (FileWriter writer = new FileWriter(file)) { writer.write(new Gson().toJson(dataList)); } log.info("【saveRawDataToFile】原始数据已保存到文件:{}", filePath); } catch (Exception e) { log.error("【saveRawDataToFile】保存原始数据到文件异常", e); // 文件保存失败不影响主流程,只记录日志 } } /** * 第三步:处理患者数据 */ private void processPatientData(List thiedInhospInfoList, String cry) { int successCount = 0; int skipCount = 0; int errorCount = 0; for (ThiedInhospInfo thiedInhospInfo : thiedInhospInfoList) { try { if (processSinglePatient(thiedInhospInfo, cry)) { successCount++; } else { skipCount++; } } catch (Exception e) { errorCount++; log.error("【processPatientData】处理患者数据异常,患者ID:{}", thiedInhospInfo.getPatiMediaNo(), e); } } log.info("【processPatientData】患者数据处理完成,成功:{},跳过:{},失败:{}", successCount, skipCount, errorCount); } /** * 构建请求头 */ private Map buildRequestHeaders(LocalDateTime startTime) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"); Map header = new HashMap<>(); header.put("x-hcsb-serviceno", "IS044056"); // header.put("x-hcsb-serviceno", "IS002347"); header.put("x-hcsb-version", "2.0"); header.put("x-hcsb-sourcesyscode", "14"); header.put("x-hcsb-token", "ArGE2JnHtxG/Zx5nrnGY4eOfMUJGGJokAJHigG1BrCY="); header.put("x-hcsb-msgdate", startTime.format(formatter)); return header; } /** * 构建请求参数 */ private Map buildRequestParams(String cry, LocalDateTime startTime, LocalDateTime endTime) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S"); Map req = new HashMap<>(); if ("1".equals(cry)) { // 出院参数 req.put("cashStartDate", startTime.format(formatter)); req.put("cashEndDate", endTime.format(formatter)); req.put("currStatus", "4"); } else if ("0".equals(cry)) { // 入院参数 req.put("admissStartDate", startTime.format(formatter)); req.put("admissEndDate", endTime.format(formatter)); req.put("currStatus", "2"); } else if ("2".equals(cry)) { // 预约记录查询 req.put("appointRegistDateBegin", startTime.format(formatter)); req.put("appointRegistDateEnd", endTime.format(formatter)); // 排班停诊 (预约状态标志(0:已预约 1:已挂号 2:已退号 3:已就诊 -1:病人取消 -2:医院取消 -3:排班停诊)) req.put("scheduleStatusFlag", "-3"); } return req; } /** * 解析响应数据 */ private List parseResponseData(String result) { try { Gson gson = new Gson(); JsonObject jsonObject = gson.fromJson(result, JsonObject.class); // 校验data字段是否为JsonObject JsonElement dataElement = jsonObject.get("data"); if (dataElement == null || !dataElement.isJsonObject()) { log.warn("【parseResponseData】响应数据中data字段为空或非对象类型,原始数据:{}", result); return new ArrayList<>(); } JsonObject dataObject = dataElement.getAsJsonObject(); JsonElement resultElement = dataObject.get("result"); if (resultElement == null || !resultElement.isJsonArray()) { log.warn("【parseResponseData】响应数据中result字段为空或非数组类型,原始数据:{}", result); return new ArrayList<>(); } JsonArray resultArray = resultElement.getAsJsonArray(); Type resultType = new TypeToken>() { }.getType(); return gson.fromJson(resultArray, resultType); } catch (Exception e) { log.error("【parseResponseData】解析响应数据异常,原始数据:{}", result, e); throw e; } } /** * 解析响应数据 */ private List parseResponseData2(String result) { try { Gson gson = new Gson(); JsonObject jsonObject = gson.fromJson(result, JsonObject.class); // 校验data字段是否为JsonObject JsonElement dataElement = jsonObject.get("data"); if (dataElement == null || !dataElement.isJsonObject()) { log.warn("【parseResponseData2】响应数据中data字段为空或非对象类型,原始数据:{}", result); return new ArrayList<>(); } JsonObject dataObject = dataElement.getAsJsonObject(); JsonElement resultElement = dataObject.get("result"); if (resultElement == null || !resultElement.isJsonArray()) { log.warn("【parseResponseData2】响应数据中result字段为空或非数组类型,原始数据:{}", result); return new ArrayList<>(); } JsonArray resultArray = resultElement.getAsJsonArray(); Type resultType = new TypeToken>() { }.getType(); return gson.fromJson(resultArray, resultType); } catch (Exception e) { log.error("【parseResponseData2】解析响应数据异常,原始数据:{}", result, e); throw e; } } /** * 处理单个患者数据 */ private boolean processSinglePatient(ThiedInhospInfo thiedInhospInfo, String cry) { // 验证身份证号 if (StringUtils.isEmpty(thiedInhospInfo.getPatiIdCardNo())) { log.warn("【processSinglePatient】患者身份证号为空,跳过处理,患者编号:{}", thiedInhospInfo.getPatiMediaNo()); return false; } // 处理患者档案信息 PatArchive patArchive = processPatientArchive(thiedInhospInfo, cry); // 处理患者住院信息 processPatientInhospInfo(thiedInhospInfo, patArchive, cry); return true; } /** * 处理患者档案信息 */ private PatArchive processPatientArchive(ThiedInhospInfo thiedInhospInfo, String cry) { // 查询患者是否已存在 PatArchive queryArchive = new PatArchive(); queryArchive.setIdcardno(thiedInhospInfo.getPatiIdCardNo().trim()); List existingArchives = patArchiveService.selectPatArchiveList(queryArchive); // 构建患者档案信息 PatArchive patArchive = buildPatientArchive(thiedInhospInfo, cry); // 保存或更新患者档案 if (CollectionUtils.isEmpty(existingArchives)) { patArchiveService.insertPatArchive(patArchive); log.debug("【processPatientArchive】新增患者档案,患者编号:{}", patArchive.getPatientno()); } else { patArchive.setId(existingArchives.get(0).getId()); patArchiveService.updateArchive(patArchive); log.debug("【processPatientArchive】更新患者档案,患者编号:{}", patArchive.getPatientno()); } return patArchive; } /** * 处理患者住院信息 */ private void processPatientInhospInfo(ThiedInhospInfo thiedInhospInfo, PatArchive patArchive, String cry) { // 构建住院信息 PatMedInhosp patMedInhosp = buildPatientInhospInfo(thiedInhospInfo, patArchive, cry); // 查询是否已存在住院记录 PatMedInhosp queryInhosp = new PatMedInhosp(); queryInhosp.setPatno(patArchive.getPatientno()); queryInhosp.setSerialnum(patMedInhosp.getSerialnum()); queryInhosp.setCry(Integer.valueOf(cry)); List existingInhosps = patMedInhospService.selectPatMedInhospList(queryInhosp); // 保存或更新住院信息 if (CollectionUtils.isNotEmpty(existingInhosps)) { patMedInhosp.setInhospid(existingInhosps.get(0).getInhospid()); patMedInhospService.updatePatMedInhosp(patMedInhosp); log.debug("【processPatientInhospInfo】更新住院信息,患者编号:{},流水号:{}", patArchive.getPatientno(), patMedInhosp.getSerialnum()); } else { patMedInhospService.insertPatMedInhosp(patMedInhosp); log.debug("【processPatientInhospInfo】新增住院信息,患者编号:{},流水号:{}", patArchive.getPatientno(), patMedInhosp.getSerialnum()); } } /** * 构建患者住院信息 */ private PatMedInhosp buildPatientInhospInfo(ThiedInhospInfo thiedInhospInfo, PatArchive patArchive, String cry) { PatMedInhosp patMedInhosp = new PatMedInhosp(); // 患者基本信息 patMedInhosp.setPatid(patArchive.getId()); patMedInhosp.setPatno(patArchive.getPatientno()); patMedInhosp.setPatname(patArchive.getName()); // 护理信息 patMedInhosp.setNurseId(thiedInhospInfo.getNurseId()); patMedInhosp.setNurseName(thiedInhospInfo.getNurseName()); // 住院信息 patMedInhosp.setSerialnum(thiedInhospInfo.getInpatientId()); patMedInhosp.setFuflag("1"); if (StringUtils.isNotEmpty(patArchive.getNotrequiredFlag()) && patArchive.getNotrequiredFlag().equals("1")) patMedInhosp.setFuflag("0"); patMedInhosp.setInhospstate("0".equals(cry) ? "0" : "1"); // 医院和床位信息 patMedInhosp.setHospitalcode(thiedInhospInfo.getAreaId()); patMedInhosp.setBedNo(thiedInhospInfo.getAdmissBedNo()); patMedInhosp.setCry(Integer.valueOf(cry)); // 时间信息 if (StringUtils.isNotEmpty(thiedInhospInfo.getAdmissDate())) { try { patMedInhosp.setStarttime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(thiedInhospInfo.getAdmissDate())); } catch (ParseException e) { log.warn("【buildPatientInhospInfo】解析入院时间失败:{}", thiedInhospInfo.getAdmissDate(), e); } } if (StringUtils.isNotEmpty(thiedInhospInfo.getOutDate())) { try { patMedInhosp.setEndtime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(thiedInhospInfo.getOutDate())); } catch (ParseException e) { log.warn("【buildPatientInhospInfo】解析出院时间失败:{}", thiedInhospInfo.getOutDate(), e); } } // 出院信息 if (cry.equals("1")) { patMedInhosp.setLeaveldeptcode(thiedInhospInfo.getCurrDeptId()); patMedInhosp.setLeaveldeptname(thiedInhospInfo.getCurrDeptName()); patMedInhosp.setLeavediagname(thiedInhospInfo.getDiagName()); patMedInhosp.setLeaveicd10code(thiedInhospInfo.getDiagIcd10()); patMedInhosp.setOutWayId(thiedInhospInfo.getOutWayId()); patMedInhosp.setOutWayName(thiedInhospInfo.getOutWayName()); // 病区信息 patMedInhosp.setLeavehospitaldistrictid(thiedInhospInfo.getCurrWardCode()); patMedInhosp.setLeavehospitaldistrictcode(thiedInhospInfo.getCurrWardId()); patMedInhosp.setLeavehospitaldistrictname(thiedInhospInfo.getCurrWardName()); patMedInhosp.setLeaveldeptid(thiedInhospInfo.getCurrDeptCode()); } else { //入院 patMedInhosp.setDeptcode(thiedInhospInfo.getCurrDeptId()); patMedInhosp.setDeptname(thiedInhospInfo.getCurrDeptName()); patMedInhosp.setDiagname(thiedInhospInfo.getDiagName()); patMedInhosp.setIcd10code(thiedInhospInfo.getDiagIcd10()); patMedInhosp.setHospitaldistrictid(thiedInhospInfo.getAdmissWardCode()); patMedInhosp.setHospitaldistrictcode(thiedInhospInfo.getAdmissWardId()); patMedInhosp.setHospitaldistrictname(thiedInhospInfo.getAdmissWardName()); } // 医生信息 patMedInhosp.setDrname(thiedInhospInfo.getDoctName()); patMedInhosp.setDrcode(thiedInhospInfo.getDoctId()); // 状态信息 patMedInhosp.setSchemestatus("0".equals(cry) ? 1L : 2L); // 系统字段 patMedInhosp.setDelFlag("0"); patMedInhosp.setOrgid("1"); return patMedInhosp; } /** * 构建患者档案信息 */ private PatArchive buildPatientArchive(ThiedInhospInfo thiedInhospInfo, String cry) { PatArchive patArchive = new PatArchive(); // 基本信息 patArchive.setPatientno(thiedInhospInfo.getPatiMediaNo()); patArchive.setIdcardno(thiedInhospInfo.getPatiIdCardNo().trim()); patArchive.setName(thiedInhospInfo.getPatiRecordName()); patArchive.setSourcefrom(2L); patArchive.setPattype("2"); // 性别 patArchive.setSex("男".equals(thiedInhospInfo.getPatiRecordGender()) ? 1L : 2L); // 民族和籍贯 patArchive.setNation(thiedInhospInfo.getPatiNation()); patArchive.setNativePlace(thiedInhospInfo.getPatiNationality()); // 居住地址 String homeAddr = thiedInhospInfo.getPatiHomeAddr(); patArchive.setPlaceOfResidence(StringUtils.isNotEmpty(homeAddr) ? homeAddr.replace("null", "") : ""); // 出生日期 if (StringUtils.isNotEmpty(thiedInhospInfo.getPatiBirthday())) { try { patArchive.setBirthdate(new SimpleDateFormat("yyyy-MM-dd").parse(thiedInhospInfo.getPatiBirthday())); } catch (ParseException e) { log.warn("【buildPatientArchive】解析出生日期失败:{}", thiedInhospInfo.getPatiBirthday(), e); } } // 年龄 if (thiedInhospInfo.getTreateAge() != null) { patArchive.setAge(Long.valueOf(thiedInhospInfo.getTreateAge())); } patArchive.setAgeUnit(thiedInhospInfo.getTreatAgeunit()); // 联系方式 patArchive.setTelcode(thiedInhospInfo.getCompanyTelNum()); patArchive.setRelativetelcode(thiedInhospInfo.getContactPersonPhone()); // 出院方式处理(死亡等特殊情况) if (StringUtils.isNotEmpty(thiedInhospInfo.getOutWayId()) && "4".equals(thiedInhospInfo.getOutWayId())) { patArchive.setNotrequiredFlag("1"); patArchive.setNotrequiredreason(thiedInhospInfo.getOutWayName()); } if ("1".equals(cry) && StringUtils.isEmpty(thiedInhospInfo.getOutWayId())) { patArchive.setNotrequiredFlag("1"); patArchive.setNotrequiredreason("出院方式为空"); } // 系统字段 patArchive.setDelFlag("0"); patArchive.setCreateTime(new Date()); patArchive.setUpdateTime(new Date()); return patArchive; } }