Bladeren bron

Merge branch 'shaoyf/dev' into shaoyf/master

rengb 5 jaren geleden
bovenliggende
commit
6c38850783
28 gewijzigde bestanden met toevoegingen van 2257 en 247 verwijderingen
  1. 211 6
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/behospitalized/BEH0011.java
  2. 23 7
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/firstcourserecord/FIRC0093.java
  3. 252 53
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR02985.java
  4. 158 127
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR02986.java
  5. 3 4
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03010.java
  6. 239 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03069.java
  7. 156 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03070.java
  8. 110 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03071.java
  9. 239 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03072.java
  10. 235 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03074.java
  11. 226 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03075.java
  12. 30 9
      kernel/src/main/java/com/lantone/qc/kernel/structure/ai/ThreeLevelWardAI.java
  13. 29 3
      kernel/src/main/java/com/lantone/qc/kernel/structure/ai/model/EntityEnum.java
  14. 68 14
      kernel/src/main/java/com/lantone/qc/kernel/structure/ai/process/EntityProcessThreeLevelWard.java
  15. 1 1
      kernel/src/main/java/com/lantone/qc/kernel/util/CatalogueUtil.java
  16. 80 0
      kernel/src/main/java/com/lantone/qc/kernel/util/SimilarityUtil.java
  17. 17 0
      public/src/main/java/com/lantone/qc/pub/model/entity/AuxiliaryExamination.java
  18. 15 0
      public/src/main/java/com/lantone/qc/pub/model/entity/AuxiliaryExaminationResults.java
  19. 15 0
      public/src/main/java/com/lantone/qc/pub/model/entity/CheckTime.java
  20. 16 0
      public/src/main/java/com/lantone/qc/pub/model/entity/Consumption.java
  21. 20 0
      public/src/main/java/com/lantone/qc/pub/model/entity/Drug.java
  22. 18 0
      public/src/main/java/com/lantone/qc/pub/model/entity/Laboratory.java
  23. 16 0
      public/src/main/java/com/lantone/qc/pub/model/entity/LaboratoryPackage.java
  24. 13 0
      public/src/main/java/com/lantone/qc/pub/model/entity/LaboratoryResults.java
  25. 16 0
      public/src/main/java/com/lantone/qc/pub/model/entity/ReasonsForAntibiotic.java
  26. 16 0
      public/src/main/java/com/lantone/qc/pub/model/entity/Stop.java
  27. 15 0
      public/src/main/java/com/lantone/qc/pub/model/entity/UsageWardRound.java
  28. 20 23
      public/src/main/java/com/lantone/qc/pub/model/label/ThreeLevelWardLabel.java

+ 211 - 6
kernel/src/main/java/com/lantone/qc/kernel/catalogue/behospitalized/BEH0011.java

@@ -1,6 +1,7 @@
 package com.lantone.qc.kernel.catalogue.behospitalized;
 
 import com.alibaba.fastjson.JSONArray;
+import com.google.common.collect.Lists;
 import com.lantone.qc.kernel.catalogue.QCCatalogue;
 import com.lantone.qc.kernel.client.ChiefPresentSimilarityServiceClient;
 import com.lantone.qc.kernel.structure.ai.ModelAI;
@@ -15,16 +16,12 @@ import com.lantone.qc.pub.model.label.PacsLabel;
 import com.lantone.qc.pub.model.label.PastLabel;
 import com.lantone.qc.pub.model.label.PresentLabel;
 import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -35,6 +32,7 @@ import java.util.stream.Collectors;
  * @Author : 楼辉荣
  * @Date: 2020-03-06 17:28
  */
+@Slf4j
 @Component
 public class BEH0011 extends QCCatalogue {
     //    @Autowired
@@ -132,6 +130,16 @@ public class BEH0011 extends QCCatalogue {
         addInitDiagHospitalName(initDiags, initialDiagDiags);
 
         String infoStr = "";
+        /*先两两对比,能对上就删除,对不上的再走相似度模型*/
+        Iterator<String> iterator = presentPastDiags.iterator();
+        while (iterator.hasNext()) {
+            String presentPastDiag = iterator.next();
+            for (String initDiag : initDiags) {
+                if (presentPastDiag.equals(initDiag) || presentPastDiag.contains(initDiag) || initDiag.contains(presentPastDiag)) {
+                    iterator.remove();
+                }
+            }
+        }
         int matchSum = 0;
         ModelAI modelAI = new ModelAI();
         for (String presentPastDiag : presentPastDiags) {
@@ -157,6 +165,7 @@ public class BEH0011 extends QCCatalogue {
         if (matchSum == presentPastDiags.size()) {
             status.set("0");
         }
+        lackDiabetesFeritin(initDiags, presentLabel.getText(), inputInfo.getBeHospitalizedDoc().getStructureMap().get("长期用药"), inputInfo.getBeHospitalizedDoc().getStructureMap().get("外院用药"), inputInfo.getMedicalRecordInfoDoc().getStructureMap().get("behospitalCode"));
         /*
         if (initDiags.containsAll(presentPastDiags)) {
             status.set("0");
@@ -236,4 +245,200 @@ public class BEH0011 extends QCCatalogue {
             status.set("0");
         }
      */
+
+    //糖尿病、高血压药品过滤
+    private void lackDiabetesFeritin(List<String> initDiags, String... texts) {
+        String checkText = "";
+        if (StringUtil.isNotBlank(texts[0])) {
+            checkText += texts[0] + "。";
+        }
+        if (StringUtil.isNotBlank(texts[1])) {
+            checkText += texts[1] + "。";
+        }
+        if (StringUtil.isNotBlank(texts[2])) {
+            checkText += texts[2] + "。";
+        }
+        if (StringUtil.isBlank(checkText)) {
+            return;
+        }
+        long diabetes = initDiags.stream().filter(x -> x.contains("糖尿病")).count();
+        for (String diabetesDrug : diabetesDrugs) {
+            if (checkText.contains(diabetesDrug) && diabetes == 0) {
+                if (StringUtil.isBlank(info.get()) || !info.get().contains("糖尿病")) {
+                    info.set(CatalogueUtil.concatInfo(info.get(), "糖尿病"));
+                    status.set("-1");
+                    log.error("初步诊断不完整:" + texts[3]);
+                }
+                break;
+            }
+        }
+        long hypertension = initDiags.stream().filter(x -> x.contains("高血压")).count();
+        for (String feritinDrug : feritinDrugs) {
+            if (checkText.contains(feritinDrug) && hypertension == 0) {
+                if (StringUtil.isBlank(info.get()) || !info.get().contains("高血压")) {
+                    info.set(CatalogueUtil.concatInfo(info.get(), "高血压"));
+                    status.set("-1");
+                    log.error("初步诊断不完整:" + texts[3]);
+                }
+                break;
+            }
+        }
+    }
+
+    //糖尿病药
+    private List<String> diabetesDrugs = Lists.newArrayList(
+            "重组人胰岛素",
+            "重组赖脯胰岛素",
+            "谷赖胰岛素",
+            "赖脯胰岛素",
+            "门冬胰岛素",
+            "生物合成人胰岛素",
+            "胰岛素",
+            "低精蛋白锌胰岛素",
+            "精蛋白生物合成人胰岛素",
+            "精蛋白锌重组人胰岛素",
+            "精蛋白重组人胰岛素",
+            "德谷门冬双胰岛素",
+            "30/70混合重组人胰岛素",
+            "50/50混合重组人胰岛素",
+            "精蛋白生物合成人胰岛素(预混30R)",
+            "精蛋白锌胰岛素(30R)",
+            "精蛋白锌混合重组赖脯胰岛素(50R)",
+            "精蛋白锌混合重组赖脯胰岛素(25R)",
+            "精蛋白重组人胰岛素(预混30/70)",
+            "精蛋白重组人胰岛素(50/50)",
+            "精蛋白锌重组人胰岛素(40/60)",
+            "门冬胰岛素30",
+            "门冬胰岛素50",
+            "重组甘精胰岛素",
+            "地特胰岛素",
+            "德谷胰岛素",
+            "甘精胰岛素",
+            "精蛋白锌胰岛素",
+            "米格列奈钙",
+            "那格列奈",
+            "瑞格列奈",
+            "吡格列酮二甲双胍",
+            "二甲双胍格列呲嗪",
+            "二甲双胍格列齐特",
+            "二甲双胍格列本脲Ⅰ",
+            "二甲双胍格列本脲Ⅱ",
+            "二甲双胍格列吡嗪",
+            "二甲双胍维格列汀",
+            "利格列汀二甲双胍",
+            "瑞格列奈二甲双胍",
+            "沙格列汀二甲双胍",
+            "西格列汀二甲双胍",
+            "格列本脲",
+            "格列吡嗪",
+            "格列喹酮",
+            "格列美脲",
+            "格列齐特",
+            "格列齐特Ⅱ",
+            "甲苯磺丁脲",
+            "吡格列酮",
+            "罗格列酮",
+            "二甲双胍",
+            "阿卡波糖",
+            "伏格列波糖",
+            "依帕司他",
+            "硫辛酸",
+            "艾塞那肽",
+            "贝那鲁肽",
+            "度拉糖肽",
+            "聚乙二醇洛塞那肽",
+            "利拉鲁肽",
+            "利司那肽",
+            "阿格列汀",
+            "利格列汀",
+            "沙格列汀",
+            "维格列汀",
+            "西格列汀"
+    );
+    //高血压药
+    private List<String> feritinDrugs = Lists.newArrayList(
+            "贝那普利",
+            "福辛普利",
+            "卡托普利",
+            "喹那普利",
+            "赖诺普利",
+            "雷米普利",
+            "咪达普利",
+            "培哚普利",
+            "依那普利",
+            "贝那普利氢氯噻嗪",
+            "复方卡托普利",
+            "赖诺普利氢氯噻嗪",
+            "培哚普利吲达帕胺",
+            "依那普利氢氯噻嗪",
+            "氨氯地平贝那普利Ⅱ",
+            "氨氯地平贝那普利Ⅰ",
+            "培哚普利氨氯地平",
+            "依那普利叶酸",
+            "奥美沙坦",
+            "阿利沙坦",
+            "厄贝沙坦",
+            "坎地沙坦",
+            "氯沙坦",
+            "替米沙坦",
+            "缬沙坦",
+            "奥美沙坦酯氢氯噻嗪",
+            "厄贝沙坦氢氯噻嗪",
+            "坎地氢噻",
+            "氯沙坦氢氯噻嗪",
+            "替米沙坦氢氯噻嗪",
+            "缬沙坦氢氯噻嗪",
+            "奥美沙坦氨氯地平",
+            "替米沙坦氨氯地平",
+            "缬沙坦氨氯地平",
+            "沙库巴曲缬沙坦",
+            "氨氯地平阿托伐他汀钙",
+            "多沙唑嗪",
+            "萘哌地尔",
+            "哌唑嗪",
+            "乌拉地尔",
+            "乌拉地尔氯化钠",
+            "卡维地洛",
+            "阿夫唑嗪",
+            "赛洛多辛",
+            "坦洛新",
+            "特拉唑嗪",
+            "复方利血平",
+            "复方利血平氨苯蝶啶",
+            "复方双嗪利血平",
+            "复方四嗪利血平",
+            "复方双肼屈嗪",
+            "复方盐酸阿米洛利",
+            "氨苯蝶啶氢氯噻嗪",
+            "氢氯噻嗪",
+            "复方地巴唑氢氯噻嗪",
+            "复方三嗪芦丁",
+            "地尔硫䓬",
+            "洛美利嗪",
+            "阿折地平",
+            "氨氯地平",
+            "氨氯地平叶酸",
+            "贝尼地平",
+            "非洛地平",
+            "拉西地平",
+            "乐卡地平",
+            "马尼地平",
+            "门冬氨酸氨氯地平",
+            "尼卡地平",
+            "尼莫地平",
+            "尼群地平",
+            "尼群洛尔",
+            "尼索地平",
+            "尼卡地平氯化钠",
+            "尼卡地平葡萄糖",
+            "硝苯地平",
+            "西尼地平",
+            "硝苯地平Ⅰ",
+            "硝苯地平Ⅱ",
+            "硝苯地平Ⅲ",
+            "硝苯地平(Ⅳ)",
+            "左旋氨氯地平",
+            "氨苯蝶啶"
+    );
+
 }

+ 23 - 7
kernel/src/main/java/com/lantone/qc/kernel/catalogue/firstcourserecord/FIRC0093.java

@@ -24,12 +24,14 @@ import java.util.Map;
 public class FIRC0093 extends QCCatalogue {
     public void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
         status.set("0");
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
         FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
-        if (firstCourseRecordDoc != null ) {
+        if (firstCourseRecordDoc != null && beHospitalizedDoc != null) {
             Map<String, String> courseRecordDocStructureMap = firstCourseRecordDoc.getStructureMap();
-            if (courseRecordDocStructureMap != null ) {
+            Map<String, String> beHospitalizedDocStructureMap = beHospitalizedDoc.getStructureMap();
+            if (courseRecordDocStructureMap != null && beHospitalizedDocStructureMap != null) {
                 String couDate = courseRecordDocStructureMap.get("记录时间");
-                String beDate = courseRecordDocStructureMap.get("入院日期");
+                String beDate = beHospitalizedDocStructureMap.get("入院日期");
                 if (StringUtils.isNotEmpty(couDate) && StringUtils.isNotEmpty(beDate)) {
                     Date date_in = StringUtil.parseDateTime(beDate);
                     Date date_out = StringUtil.parseDateTime(couDate);
@@ -39,13 +41,27 @@ public class FIRC0093 extends QCCatalogue {
                     boolean firc = CatalogueUtil.compareTime(date_in, date_out, 8 * 60L);
                     if (firc) {
                         status.set("-1");
-                        return;
                     }
                 }
             }
-        }else {
-            status.set("-1");
-            return;
+        } else {
+            if (beHospitalizedDoc != null) {
+                Map<String, String> beHospitalizedDocStructureMap = beHospitalizedDoc.getStructureMap();
+                if (beHospitalizedDocStructureMap != null) {
+                    String beDate = beHospitalizedDocStructureMap.get("入院日期");
+                    if (StringUtils.isNotEmpty(beDate)) {
+                        Date date_in = StringUtil.parseDateTime(beDate);
+                        Date date_out = new Date();
+                        if (date_in == null) {
+                            return;
+                        }
+                        boolean firc = CatalogueUtil.compareTime(date_in, date_out, 8 * 60L);
+                        if (firc) {
+                            status.set("-1");
+                        }
+                    }
+                }
+            }
         }
     }
 }

+ 252 - 53
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR02985.java

@@ -1,27 +1,34 @@
 package com.lantone.qc.kernel.catalogue.threelevelward;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.lantone.qc.kernel.catalogue.QCCatalogue;
 import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.kernel.util.SimilarityUtil;
 import com.lantone.qc.pub.model.InputInfo;
 import com.lantone.qc.pub.model.OutputInfo;
-import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
-import com.lantone.qc.pub.model.doc.FirstCourseRecordDoc;
-import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.*;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationResultsDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDiscussionDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.model.entity.Drug;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.DateUtil;
 import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 /**
  * @ClassName : THR02985
  * @Description : 医嘱有抗生素使用病程无记录
- * 药品类型(0.普药 1.抗生素 2.素)
+ * 药品类型(0.普药 1.抗生素 2.抗生素)
  * @Author : 胡敬
  * @Date: 2020-06-23 10:04
  */
@@ -39,86 +46,278 @@ public class THR02985 extends QCCatalogue {
             "利福平胶囊",
             "异烟肼针"
     };
+    @Autowired
+    SimilarityUtil similarityUtil;
 
     public void start(InputInfo inputInfo, OutputInfo outputInfo) {
         List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
         List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
         if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
             status.set("0");
             return;
         }
-        Map<Date, String> doctorAdviceDrugMap = new HashMap<>();
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        if (allDoctorWradDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
+        Map<Date, String> doctorAdviceDrugMap = Maps.newLinkedHashMap();
         for (DoctorAdviceDoc adviceDoc : doctorAdviceDocs) {
             Map<String, String> adviceDocStructureMap = adviceDoc.getStructureMap();
             String name = adviceDocStructureMap.get("医嘱项目名称");
             String drugCategory = adviceDocStructureMap.get("药品类型");
             String startDateStr = adviceDocStructureMap.get("医嘱开始时间");
-            if (StringUtil.isNotBlank(drugCategory) && drugCategory.equals("抗生素")) {
+            if (StringUtil.isNotBlank(drugCategory) && drugCategory.contains("抗生素")) {
                 if (StringUtil.isNotBlank(name)) {
+                    name = name.replaceAll("[针]", "");
+                    if (name.contains(" ")) {
+                        name = name.split(" ")[0];
+                    }
                     if (Arrays.asList(KSS).contains(name)) {
                         continue;
                     }
-                    doctorAdviceDrugMap.put(StringUtil.parseDateTime(startDateStr), name);
+                    Date startDate = StringUtil.parseDateTime(startDateStr);
+                    if (startDate != null) {
+                        startDate = DateUtil.dateZeroClear(startDate);
+                        doctorAdviceDrugMap.put(startDate, name);
+                    }
                 }
             }
         }
-        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
-        if (allDoctorWradDocs.size() == 0) {
-            status.set("0");
-            return;
+
+        Map<String, Date> info = Maps.newLinkedHashMap();
+        //入院记录中获取信息
+        if (beHospitalizedDoc != null) {
+            Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+            getInfo(info, structureMap, "入院记录", "入院日期", "治疗计划");
         }
+        //从首程治疗计划中获取信息
+        if (firstCourseRecordDoc != null) {
+            Map<String, String> structureMap = firstCourseRecordDoc.getStructureMap();
+            getInfo(info, structureMap, "首次病程录", "记录时间", "治疗计划");
+        }
+
+        //从手术记录中获取信息
+        if (operationDocs.size() > 0) {
+            //手术记录
+            List<Map<String, String>> operationRecordStructMap = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationRecordDoc)
+                    .filter(Objects::nonNull)
+                    .map(OperationRecordDoc::getStructureMap)
+                    .collect(Collectors.toList());
+            operationRecordStructMap.forEach(x -> getInfo(info, x, "手术记录", "病历日期", "手术经过"));
+            //术后首程
+            List<Map<String, String>> operationDiscussionStructMap = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .map(OperationDiscussionDoc::getStructureMap)
+                    .collect(Collectors.toList());
+            operationDiscussionStructMap.forEach(x -> getInfo(info, x, "术后首程", "记录日期", "手术经过", "治疗计划和措施"));
+        }
+
+        //从会诊记录中获取信息
+        if (consultationDocs.size() > 0) {
+            List<Map<String, String>> consultationResultStructMap = consultationDocs
+                    .stream()
+                    .map(ConsultationDoc::getConsultationResultsDoc)
+                    .filter(Objects::nonNull)
+                    .map(ConsultationResultsDoc::getStructureMap)
+                    .collect(Collectors.toList());
+            consultationResultStructMap.forEach(x -> getInfo(info, x, "会诊结果单", "会诊日期及时间", "会诊意见"));
+        }
+
+        //从出院小结中获取信息
+        if (leaveHospitalDoc != null) {
+            Map<String, String> structureMap = leaveHospitalDoc.getStructureMap();
+            getInfo(info, structureMap, "出院小结", "出院时间", "诊治经过");
+        }
+
         String infoStr = "";
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         for (Map.Entry<Date, String> doctorAdviceDrug : doctorAdviceDrugMap.entrySet()) {
             Date doctorAdviceDate = doctorAdviceDrug.getKey();
-            Set<String> splitDrugs = CatalogueUtil.getRegexWords(doctorAdviceDrug.getValue(), "[((\\[][^\\[\\]()()]+[\\]))]");
-
-            boolean isFind = false;
-            for (String drug : splitDrugs) {
-                int matchSum = 0;
-                if (drug.equals("合资") || drug.equals("进口") || drug.equals("国产")) {
-                    continue;
+            String drugs = doctorAdviceDrug.getValue();
+            drugs = removeBracket(drugs);
+            Set<String> splitDrugs = CatalogueUtil.getRegexWords(drugs, "[((\\[][^\\[\\]()()]+[\\]))]")
+                    .stream().filter(x -> !x.equals("合资") && !x.equals("进口") && !x.equals("国产")).collect(Collectors.toSet());
+            String missDrug = "";
+            for (Map.Entry<String, Date> map : info.entrySet()) {
+                missDrug = getMissDrug(map.getKey(), map.getValue(), doctorAdviceDate, splitDrugs, 2, missDrug);
+                //当前抗生素药(drugs)在info中已找到,直接跳出当前循环
+                if (StringUtil.isBlank(missDrug)) {
+                    break;
                 }
-                matchSum = getMatchSum(inputInfo.getFirstCourseRecordDoc(), allDoctorWradDocs, doctorAdviceDate, drug.replace("针", ""), matchSum, 2);
-                if (matchSum > 0) {
-                    isFind = true;
+            }
+            if (StringUtil.isBlank(missDrug)) {
+                continue;
+            }
+            boolean modelFind = false;
+            int wardNum = 0;
+            for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+                /*****************纯文本匹配******************/
+                Map<String, String> wardDocStructureMap = threeLevelWardDoc.getStructureMap();
+                String wardDateStr = wardDocStructureMap.get("查房日期");
+                String content = wardDocStructureMap.get("病情记录") + wardDocStructureMap.get("治疗计划和措施");
+                Date wardDate = StringUtil.parseDateTime(wardDateStr);
+                missDrug = getMissDrug(content, wardDate, doctorAdviceDate, splitDrugs, 2, missDrug);
+                //当前抗生素药(drugs)在查房记录中已找到,直接跳出当前查房记录的循环
+                /*****************药品相似度模型******************/
+                List<ThreeLevelWardLabel> label = threeLevelWardDoc.getThreeLevelWardLabel();
+                if (label.size() > 0) {
+                    List<Drug> drugList = label.get(0).getDrugs();
+                    for (Drug drug : drugList) {
+                        for (String adDrug : splitDrugs) {
+                            if (compareStandard(drug.getName(), adDrug)) {
+                                modelFind = true;
+                                break;
+                            }
+                        }
+                        if (modelFind) {
+                            break;
+                        }
+                    }
+                }
+                if (StringUtil.isNotBlank(missDrug) && !modelFind) {//missDrug不为空,只有两种可能:1.确实是缺失的药    2.字符串为“时间不匹配”
+                    wardNum++;                                      //当前所有查房记录都没找到药,该医嘱时间再与系统当前时间对比,若超过48小时则提示该药缺失
+                }
+                if (StringUtil.isBlank(missDrug) || modelFind) {
+                    break;
                 }
             }
-            if (!isFind) {
-                String.format(infoStr = CatalogueUtil.concatInfo(infoStr, doctorAdviceDrug.getValue() + "<医嘱时间>:" + sdf.format(doctorAdviceDate)));
+            //如果目前所有查房记录都没有
+            if (wardNum == allDoctorWradDocs.size() && CatalogueUtil.compareTime(doctorAdviceDate, new Date(), 48 * 60L)) {
+                infoStr = CatalogueUtil.concatInfo(infoStr, splitDrugs.toString().replaceAll("[\\[\\]]", "") + "(" + DateUtil.formatDate(doctorAdviceDate) + ")");
+                continue;
             }
-        }
 
+            if (StringUtil.isNotBlank(missDrug) && !"时间不匹配".equals(missDrug) && !modelFind) {
+                infoStr = CatalogueUtil.concatInfo(infoStr, missDrug + "(" + DateUtil.formatDate(doctorAdviceDate) + ")");
+            }
+        }
         if (StringUtil.isNotBlank(infoStr)) {
-            status.set("-1");
-            info.set(infoStr);
+            this.status.set("-1");
+            this.info.set(infoStr);
         } else {
-            status.set("0");
+            this.status.set("0");
         }
     }
 
-    private int getMatchSum(FirstCourseRecordDoc firstCourseRecordDoc, List<ThreeLevelWardDoc> allDoctorWradDocs, Date doctorAdviceDate, String drug, int matchSum, int days) {
-        if (firstCourseRecordDoc != null) {
-            String wardDateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
-            String content = firstCourseRecordDoc.getStructureMap().get("治疗计划") + firstCourseRecordDoc.getStructureMap().get("诊疗计划");
-            Date wardDate = StringUtil.parseDateTime(wardDateStr);
-            if (doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L)) {
-                if (content.contains(drug)) {
-                    matchSum++;
-                }
+    /**
+     * 获取各模块信息<入院记录、首次病程录、手术记录、术后首程、会诊结果单、查房记录、出院小结>
+     *
+     * @param structureMap
+     * @param info
+     */
+    private void getInfo(Map<String, Date> info, Map<String, String> structureMap, String modelType, String dateKey, String... contentKey) {
+        String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList(contentKey));
+        String recordDateStr = structureMap.get(dateKey);
+        if (StringUtil.isNotBlank(recordDateStr)) {
+            Date date = StringUtil.parseDateTime(recordDateStr);
+            if (StringUtil.isNotBlank(content) && date != null) {
+                info.put(modelType + "->" + content, date);
             }
         }
-        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
-            Map<String, String> wardDocStructureMap = threeLevelWardDoc.getStructureMap();
-            String wardDateStr = wardDocStructureMap.get("查房日期");
-            String content = wardDocStructureMap.get("病情记录") + wardDocStructureMap.get("治疗计划和措施");
-            Date wardDate = StringUtil.parseDateTime(wardDateStr);
-            if (doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L)) {
-                if (content.contains(drug)) {
-                    matchSum++;
+    }
+
+    /**
+     * 核心:从文本中找药
+     *
+     * @param content          文本
+     * @param wardDate
+     * @param doctorAdviceDate
+     * @param drugs
+     * @param days
+     * @return 如果文本中找到该药,则返回空字符串
+     */
+    private String getMissDrug(String content, Date wardDate, Date doctorAdviceDate, Set<String> drugs, int days, String missDrug) {
+        if ("时间不匹配".equals(missDrug)) {
+            missDrug = "";//初始化缺失药物
+        }
+        //开医嘱时间起,昨天今天明天记录内查找药
+        if ((doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L))
+                || (wardDate.before(doctorAdviceDate) && !CatalogueUtil.compareTime(wardDate, doctorAdviceDate, 24 * 60L))) {
+            boolean findDrug = false;
+            for (String drug : drugs) {
+                if (StringUtil.isBlank(drug)) {
+                    continue;
                 }
+                if (content.contains(drug.replaceAll("[^\\u4e00-\\u9fa5]", ""))
+                        || regexFind(content, "继续", "治疗") || regexFind(content, "维持", "治疗")
+                        || regexFind(content, "继续", "抗感染") || regexFind(content, "治疗", "同前")) {
+                    findDrug = true;
+                    break;
+                } else {
+                    missDrug = concatInfo(missDrug, drug);
+                }
+            }
+            if (findDrug) {
+                missDrug = "";//如果找到一种抗生素药,就把报错信息置为空
+            }
+        } else {
+            if (StringUtil.isBlank(missDrug)) {
+                missDrug = "时间不匹配";
+            }
+        }
+        return missDrug;
+    }
+
+    private String concatInfo(String infoStr, String content) {
+        if (StringUtil.isBlank(infoStr)) {
+            infoStr += content;
+        } else {
+            if (!infoStr.contains(content)) {
+                infoStr += "或" + content;
             }
         }
-        return matchSum;
+        return infoStr;
+    }
+
+    private boolean regexFind(String content, String... str) {
+        String s = "";
+        for (String word : str) {
+            s += word + ".*";
+        }
+        s = s.substring(0, s.lastIndexOf(".*"));
+        Pattern p = Pattern.compile(s);
+        Matcher m = p.matcher(content);
+        return m.find();
+    }
+
+    /**
+     * 比较两个抗生素标准词是否一致
+     *
+     * @param firstWord
+     * @param secordWord
+     * @return
+     */
+    private boolean compareStandard(String firstWord, String secordWord) {
+        if (StringUtil.isBlank(firstWord) || StringUtil.isBlank(secordWord)) {
+            return false;
+        }
+        String drugStandardWord1 = similarityUtil.getDrugStandardWord(firstWord);
+        String drugStandardWord2 = similarityUtil.getDrugStandardWord(secordWord);
+        if (drugStandardWord1 == null || drugStandardWord2 == null) {
+            return firstWord.equals(secordWord) || firstWord.contains(secordWord) || secordWord.contains(firstWord);
+        }
+        return drugStandardWord1.equals(drugStandardWord2);
+    }
+
+    /**
+     * 如果文本包含中括号([海正]美罗培南针),取括号之后的文字
+     *
+     * @param str
+     * @return
+     */
+    private String removeBracket(String str) {
+        if (str.contains("]") && str.indexOf("]") != str.length() - 1) {
+            return str.substring(str.indexOf("]") + 1);
+        }
+        return str;
     }
 }

+ 158 - 127
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR02986.java

@@ -1,87 +1,162 @@
 package com.lantone.qc.kernel.catalogue.threelevelward;
 
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
 import com.lantone.qc.kernel.catalogue.QCCatalogue;
 import com.lantone.qc.kernel.util.CatalogueUtil;
 import com.lantone.qc.pub.model.InputInfo;
 import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.BeHospitalizedDoc;
 import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
 import com.lantone.qc.pub.model.doc.FirstCourseRecordDoc;
 import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDiscussionDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
 import com.lantone.qc.pub.util.StringUtil;
-import org.apache.commons.lang3.StringUtils;
 import org.springframework.stereotype.Component;
 
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * @ClassName : THR02986
  * @Description : 医嘱有激素使用病程无记录
- * 药品类型(0.普药 1.抗生素 2.激素)
+ * 药品类型(0.普药 1.素 2.激素)
  * @Author : 胡敬
- * @Date: 2020-06-23 10:04
+ * @Date: 2020-06-23 10:43
  */
 @Component
 public class THR02986 extends QCCatalogue {
-
     public void start(InputInfo inputInfo, OutputInfo outputInfo) {
         List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
         List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
         if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
             status.set("0");
             return;
         }
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        if (allDoctorWradDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
         Map<Date, String> doctorAdviceDrugMap = new HashMap<>();
+        String regex = "[()*+-]";
         for (DoctorAdviceDoc adviceDoc : doctorAdviceDocs) {
             Map<String, String> adviceDocStructureMap = adviceDoc.getStructureMap();
             String name = adviceDocStructureMap.get("医嘱项目名称");
             String drugCategory = adviceDocStructureMap.get("药品类型");
             String startDateStr = adviceDocStructureMap.get("医嘱开始时间");
-            if (StringUtil.isNotBlank(drugCategory) && drugCategory.equals("激素")) {
-
+            if (StringUtil.isNotBlank(drugCategory) && drugCategory.contains("激素")) {
                 if (StringUtil.isNotBlank(name)) {
-                    if (Arrays.asList(JS).contains(name)) {
-                        continue;
+                    if (name.contains(" ")) {
+                        name = name.split(" ")[0];
                     }
                     doctorAdviceDrugMap.put(StringUtil.parseDateTime(startDateStr), name);
                 }
             }
         }
-        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
-        if (allDoctorWradDocs.size() == 0) {
-            status.set("0");
-            return;
+
+        //入院记录中找药
+        String behospContent = null, firstCourseContent = null, operContent = null, afterOperContent = null;
+        Date behospDate = null, firstCourseDate = null, operDate = null, afterOperDate = null;
+        if (beHospitalizedDoc != null) {
+            behospContent = CatalogueUtil.structureMapJoin(beHospitalizedDoc.getStructureMap(), Lists.newArrayList("治疗计划"));
+            String recordDateStr = firstCourseRecordDoc.getStructureMap().get("入院日期");
+            if (StringUtil.isNotBlank(recordDateStr)) {
+                behospDate = StringUtil.parseDateTime(recordDateStr);
+            }
+        }
+        //从首程治疗计划中找药
+        if (firstCourseRecordDoc != null) {
+            firstCourseContent = firstCourseRecordDoc.getStructureMap().get("治疗计划");
+            String recordDateStr = firstCourseRecordDoc.getStructureMap().get("记录时间");
+            if (StringUtil.isNotBlank(recordDateStr)) {
+                firstCourseDate = StringUtil.parseDateTime(recordDateStr);
+            }
+        }
+
+        Map<String, Date> operInfo = Maps.newHashMap();
+        if (operationDocs.size() > 0) {
+            for (OperationDoc oDoc : operationDocs) {
+                OperationRecordDoc operationRecordDoc = oDoc.getOperationRecordDoc();
+                if (operationRecordDoc != null) {
+                    operContent = operationRecordDoc.getStructureMap().get("手术经过");
+                    String operDateStr = operationRecordDoc.getStructureMap().get("手术日期");
+                    if (StringUtil.isNotBlank(operDateStr)) {
+                        operDate = StringUtil.parseDateTime(operDateStr);
+                    }
+                    if (StringUtil.isNotBlank(operContent) && operDate != null) {
+                        operInfo.put(operContent, operDate);
+                    }
+                }
+                OperationDiscussionDoc operationDiscussionDoc = oDoc.getOperationDiscussionDoc();
+                if (operationDiscussionDoc != null) {
+                    afterOperContent = operationDiscussionDoc.getStructureMap().get("手术经过");
+                    String operDateStr = operationDiscussionDoc.getStructureMap().get("手术日期");
+                    if (StringUtil.isNotBlank(operDateStr)) {
+                        afterOperDate = StringUtil.parseDateTime(operDateStr);
+                    }
+                    if (StringUtil.isNotBlank(afterOperContent) && afterOperDate != null) {
+                        operInfo.put(afterOperContent, afterOperDate);
+                    }
+                }
+            }
         }
+
         String infoStr = "";
-        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd");
         for (Map.Entry<Date, String> doctorAdviceDrug : doctorAdviceDrugMap.entrySet()) {
             Date doctorAdviceDate = doctorAdviceDrug.getKey();
-            String format = sdf1.format(doctorAdviceDate);
-            doctorAdviceDate = StringUtil.parseDateTime(format);
-            Set<String> splitDrugs = CatalogueUtil.getRegexWords(doctorAdviceDrug.getValue(), "[((\\[][^\\[\\]()()]+[\\]))]");
-
-            boolean isFind = false;
-            for (String drug : splitDrugs) {
-                int matchSum = 0;
-                if (drug.equals("合资") || drug.equals("进口") || drug.equals("国产")) {
+            String drugs = doctorAdviceDrug.getValue();
+            String[] drugArr = drugs.split(regex);
+            String missDrug = "";
+            //入院记录中找药
+            if (StringUtil.isNotBlank(behospContent) && behospDate != null) {
+                missDrug = getMissDrug(behospContent, behospDate, doctorAdviceDate, drugArr, 2, missDrug);
+                if (StringUtil.isBlank(missDrug)) {//文本中已找到该激素
                     continue;
                 }
-                matchSum = getMatchSum(inputInfo.getFirstCourseRecordDoc(), allDoctorWradDocs, doctorAdviceDate, drug, matchSum, 2);
-                if (matchSum > 0) {
-                    isFind = true;
+            }
+            //从首程治疗计划中找药
+            if (StringUtil.isNotBlank(firstCourseContent) && firstCourseDate != null) {
+                missDrug = getMissDrug(firstCourseContent, firstCourseDate, doctorAdviceDate, drugArr, 2, missDrug);
+                if (StringUtil.isBlank(missDrug)) {
+                    continue;
                 }
             }
-            if (!isFind) {
-                String.format(infoStr = CatalogueUtil.concatInfo(infoStr, doctorAdviceDrug.getValue() + "<医嘱时间>:" + sdf.format(doctorAdviceDate)));
+            for (Map.Entry<String, Date> info : operInfo.entrySet()) {
+                missDrug = getMissDrug(info.getKey(), info.getValue(), doctorAdviceDate, drugArr, 2, missDrug);
+                //当前激素药(drugs)在手术记录或术后首次病程录中已找到,直接跳出当前循环
+                if (StringUtil.isBlank(missDrug)) {
+                    break;
+                }
+            }
+            if (StringUtil.isBlank(missDrug)) {
+                continue;
+            }
+            for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+                Map<String, String> wardDocStructureMap = threeLevelWardDoc.getStructureMap();
+                String wardDateStr = wardDocStructureMap.get("查房日期");
+                String content = wardDocStructureMap.get("病情记录") + wardDocStructureMap.get("治疗计划和措施");
+                Date wardDate = StringUtil.parseDateTime(wardDateStr);
+                missDrug = getMissDrug(content, wardDate, doctorAdviceDate, drugArr, 2, missDrug);
+                //当前激素药(drugs)在查房记录中已找到,直接跳出当前查房记录的循环
+                if (StringUtil.isBlank(missDrug)) {
+                    break;
+                }
             }
-        }
 
+            if (StringUtil.isNotBlank(missDrug) && !"时间不匹配".equals(missDrug)) {
+                infoStr = CatalogueUtil.concatInfo(infoStr, missDrug);
+            }
+        }
         if (StringUtil.isNotBlank(infoStr)) {
             status.set("-1");
             info.set(infoStr);
@@ -90,108 +165,64 @@ public class THR02986 extends QCCatalogue {
         }
     }
 
-    private int getMatchSum(FirstCourseRecordDoc firstCourseRecordDoc, List<ThreeLevelWardDoc> allDoctorWradDocs, Date doctorAdviceDate, String drug, int matchSum, int days) {
-        if (firstCourseRecordDoc != null) {
-            String wardDateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
-            String content = firstCourseRecordDoc.getStructureMap().get("治疗计划") + firstCourseRecordDoc.getStructureMap().get("诊疗计划");
-            Date wardDate = StringUtil.parseDateTime(wardDateStr);
-            if (doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L)) {
-                if (content.contains(drug)) {
-                    matchSum++;
+    /**
+     * 从文本中找药
+     *
+     * @param content          文本
+     * @param wardDate
+     * @param doctorAdviceDate
+     * @param drugs
+     * @param days
+     * @return 如果文本中找到该药,则返回空字符串
+     */
+    private String getMissDrug(String content, Date wardDate, Date doctorAdviceDate, String[] drugs, int days, String missDrug) {
+        if ("时间不匹配".equals(missDrug)) {
+            missDrug = "";//初始化缺失药物
+        }
+        if (doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L)) {
+            boolean findDrug = false;
+            for (String drug : drugs) {
+                if (StringUtil.isBlank(drug)) {
+                    continue;
+                }
+                if (content.contains(drug.replaceAll("[^\\u4e00-\\u9fa5]", ""))
+                        || regexFind(content, "继续", "治疗") || regexFind(content, "维持", "治疗")) {
+                    findDrug = true;
+                    break;
+                } else {
+                    missDrug = concatInfo(missDrug, drug);
                 }
             }
-        }
-        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
-            Map<String, String> wardDocStructureMap = threeLevelWardDoc.getStructureMap();
-            String wardDateStr = wardDocStructureMap.get("查房日期");
-            if (StringUtils.isNotBlank(wardDateStr)) {
-                wardDateStr = wardDateStr.split(" ")[0];
+            if (findDrug) {
+                missDrug = "";//如果找到一种激素药,就把报错信息置为空
             }
-            String content = wardDocStructureMap.get("病情记录") + wardDocStructureMap.get("治疗计划和措施");
-            Date wardDate = StringUtil.parseDateTime(wardDateStr);
-            if ((doctorAdviceDate.before(wardDate) || doctorAdviceDate.equals(wardDate)) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L)) {
-                if (content.contains(drug)) {
-                    matchSum++;
-                }
+        } else {
+            if (StringUtil.isBlank(missDrug)) {
+                missDrug = "时间不匹配";
             }
         }
-        return matchSum;
+        return missDrug;
     }
 
-    private static final String[] JS = { "瑞格列奈片", "西格列汀片", "维格列汀片", "伏格列波糖片", "[合资]二甲双胍片"
-            , "[进口]硫辛酸针", "[合资]阿卡波糖片", "格列齐特缓释片", "达格列净片", "[合资]格列美脲片", "[绿叶]阿卡波糖胶囊"
-            , "[海汇]格列美脲片", "格列吡嗪缓释胶囊", "[国产]阿卡波糖片", "格列吡嗪控释片", "[国产]二甲双胍片", "沙格列汀片"
-            , "吡格列酮二甲双胍片", "[艾可拓]吡格列酮片", "阿仑膦酸钠维D3片", "[卡司平]吡格列酮片", "[国产]硫辛酸针"
-            , "格列喹酮片", "[国产]阿仑膦酸钠肠溶片", "那格列奈片", "[自备]格列齐特-II片",
-            "[特]门冬胰岛素针",
-            "[预填充]甘精胰岛素针",
-            "[优泌乐50]精蛋白锌赖脯胰岛素针",
-            "[特30]门冬胰岛素30针",
-            "左旋甲状腺素片",
-            "胰岛素针",
-            "[进口]奥曲肽针",
-            "[优泌乐25]精蛋白锌赖脯胰岛素针",
-            "[国产]生长抑素针",
-            "[国产]特利加压素针",
-            "[国产]奥曲肽针",
-            "[诺和灵30R]精蛋白生物合成人胰岛素针",
-            "谷赖胰岛素针",
-            "地特胰岛素针",
-            "[进口]生长抑素针",
-            "甲巯咪唑片",
-            "降钙素针",
-            "[优泌乐]赖脯胰岛素针",
-            "利拉鲁肽针",
-            "重组甘精胰岛素针",
-            "[70/30]精蛋白锌重组人胰岛素针",
-            "[预灌封优泌乐50]精蛋白锌赖脯胰岛素针",
-            "黄体酮针",
-            "地屈孕酮片",
-            "50/50混合重组人胰岛素针",
-            "炔诺酮片",
-            "[进口]特利加压素针",
-            "戊酸雌二醇片",
-            "4.5iu生长激素针",
-            "生物合成人胰岛素针",
-            "米非司酮片",
-            "[进口]黄体酮软胶囊",
-            "十一酸睾酮胶囊",
-            "[芯中效]精蛋白锌重组人胰岛素针",
-            "[国产]丙硫氧嘧啶片",
-            "[国产]黄体酮胶囊",
-            "[芯常规]重组人胰岛素针",
-            "[自备]重组甘精胰岛素针",
-            "[密盖息]降钙素鼻喷剂",
-            "甲羟孕酮片",
-            "替勃龙片",
-            "[金尔力]降钙素鼻喷剂",
-            "雷洛昔芬片",
-            "瑞格列奈片",
-            "西格列汀片",
-            "维格列汀片",
-            "伏格列波糖片",
-            "[合资]二甲双胍片",
-            "[进口]硫辛酸针",
-            "[合资]阿卡波糖片",
-            "格列齐特缓释片",
-            "达格列净片",
-            "[合资]格列美脲片",
-            "[绿叶]阿卡波糖胶囊",
-            "[海汇]格列美脲片",
-            "格列吡嗪缓释胶囊",
-            "[国产]阿卡波糖片",
-            "格列吡嗪控释片",
-            "[国产]二甲双胍片",
-            "吡格列酮二甲双胍片",
-            "沙格列汀片",
-            "[艾可拓]吡格列酮片",
-            "阿仑膦酸钠维D3片",
-            "[卡司平]吡格列酮片",
-            "[国产]硫辛酸针",
-            "格列喹酮片",
-            "[国产]阿仑膦酸钠肠溶片",
-            "那格列奈片",
-            "[自备]格列齐特-II片"
-    };
+    private String concatInfo(String infoStr, String content) {
+        if (StringUtil.isBlank(infoStr)) {
+            infoStr += content;
+        } else {
+            if (!infoStr.contains(content)) {
+                infoStr += "或" + content;
+            }
+        }
+        return infoStr;
+    }
 
+    private boolean regexFind(String content, String... str) {
+        String s = "";
+        for (String word : str) {
+            s += word + ".*";
+        }
+        s = s.substring(0, s.lastIndexOf(".*"));
+        Pattern p = Pattern.compile(s);
+        Matcher m = p.matcher(content);
+        return m.find();
+    }
 }

+ 3 - 4
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03010.java

@@ -3,7 +3,6 @@ package com.lantone.qc.kernel.catalogue.threelevelward;
 import com.lantone.qc.kernel.catalogue.QCCatalogue;
 import com.lantone.qc.pub.model.InputInfo;
 import com.lantone.qc.pub.model.OutputInfo;
-import com.lantone.qc.pub.model.doc.FirstPageRecordDoc;
 import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
 import com.lantone.qc.pub.util.DateUtil;
 import com.lantone.qc.pub.util.StringUtil;
@@ -24,17 +23,17 @@ import java.util.Map;
 public class THR03010 extends QCCatalogue {
     public void start(InputInfo inputInfo, OutputInfo outputInfo) {
         status.set("0");
-        if (inputInfo.getMedicalRecordInfoDoc() == null || inputInfo.getMedicalRecordInfoDoc().getStructureMap().size() == 0) {
+        if (inputInfo.getBeHospitalizedDoc() == null || inputInfo.getBeHospitalizedDoc().getStructureMap().size() == 0) {
             return;
         }
         if (inputInfo.getFirstPageRecordDoc() == null || inputInfo.getFirstPageRecordDoc().getStructureMap().size() == 0) {
             return;
         }
         Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
-        String behospitalDate = structureMap.get("入院日期") == null ? null : structureMap.get("入院日期");
+        String behospitalDate = structureMap.get("入院日期");
         //String leaveHospitalDate = structureMap.get("leaveHospitalDate") == null ? null : structureMap.get("leaveHospitalDate");
         Map<String, String> structureMaps = inputInfo.getFirstPageRecordDoc().getStructureMap();
-        String leaveHospitalDate = structureMaps.get("出院时间")== null ? null : structureMap.get("leaveHospitalDate");
+        String leaveHospitalDate = structureMaps.get("出院时间");
         if (behospitalDate != null && leaveHospitalDate != null) {
             Date beDate = StringUtil.parseDateTime(behospitalDate);
             Date leaveDate = StringUtil.parseDateTime(leaveHospitalDate);

+ 239 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03069.java

@@ -0,0 +1,239 @@
+package com.lantone.qc.kernel.catalogue.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.kernel.util.SimilarityUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.entity.Drug;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author HUJING
+ * @create 2020-08-21 10:21
+ * @desc 抗生素加用原因不明确
+ **/
+@Component
+public class THR03069 extends QCCatalogue {
+    @Autowired
+    SimilarityUtil similarityUtil;
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
+            return;
+        }
+        //抗生素及开医嘱时间(包括加用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDate = Maps.newHashMap();
+        //抗生素加用集合   key:抗生素名    value:  0:未加用,1及以上:加用次数
+        Map<String, Integer> antibioticStatus = Maps.newHashMap();
+        //抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValue = Maps.newHashMap();
+
+        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
+                .stream()
+                .filter(Objects::nonNull)
+                .map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("抗生素") && StringUtil.isNotBlank(x.get("医嘱单次剂量")))
+                .collect(Collectors.toList());
+
+        docAdvStruct
+                .stream()
+                .map(x -> x.get("医嘱项目名称"))
+                .forEach(y -> antibioticStatus.put(removeBracket(y), 0));
+
+        String drugName = null, value = null, startDateStr = null;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            drugName = removeBracket(drugName);
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugName = drugStandardWord;
+            }
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没加用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+            }
+        }
+        //抗生素加用过的集合如果为空,则一个抗生素都没有加用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //查房记录中抗生素及查房时间(包括加用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateWard = Maps.newHashMap();
+        //查房记录中抗生素加用集合   key:抗生素名    value:  0:未加用,1及以上:加用次数
+        Map<String, Integer> antibioticStatusWard = Maps.newHashMap();
+        //查房记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueWard = Maps.newHashMap();
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+            if (doc.getThreeLevelWardLabel().size() == 0) {
+                continue;
+            }
+            ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+            List<Drug> drugs = label.getDrugs();
+            for (Drug drug : drugs) {
+                String wardDrug = drug.getName();
+                wardDrug = removeBracket(wardDrug);
+                String drugStandardWord = similarityUtil.getDrugStandardWord(wardDrug);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    wardDrug = drugStandardWord;
+                }
+                //药品用量和使用原因都有时
+                if (drug.getConsumption() != null) {
+                    //查房记录抗生素加用过的集合中没包含该抗生素,则认为该抗生素是第一次出现,此时不需要加用原因
+                    if (!antibioticStatusWard.containsKey(wardDrug) || drug.getUsageWardRound() != null) {
+                        String consumption = drug.getConsumption().getName();
+                        collectAntibioticInfo(antibioticDateWard, antibioticStatusWard, antibioticValueWard, wardDrug, consumption, doc.getStructureMap().get("查房日期"));
+                    }
+                }
+            }
+        }
+        //把查房记录中没加用过的抗生素删除
+        /*for (Map.Entry<String, Integer> as : antibioticStatusWard.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDateWard.remove(as.getKey());
+            }
+        }*/
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有加用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有已加用并且该抗生素有加用原因的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素加用时的时间往后两天内,查房记录中该抗生素加用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中加用过的抗生素,查房记录中没出现过,则该抗生素会报出来(存入miss,规则最后会把所有不符合的抗生素都报出来)
+         */
+        List<String> miss = Lists.newArrayList();
+        String drugKey = null, start = null, change = null, wardStartStr = null, wardChangeStr = null;
+        List<String> dateList = null;
+        for (Map.Entry<String, List<String>> ad : antibioticDate.entrySet()) {
+            drugKey = ad.getKey();
+            if (!antibioticDateWard.containsKey(drugKey)) {
+                miss.add(drugKey);
+                continue;
+            }
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);
+            List<Double> antibioticValueWardList = antibioticValueWard.get(drugKey);
+            int findNum = 0;
+            for (int i = 1; i < antibioticValueList.size(); i++) {//从加用的值开始,如果加用过的值查房记录中都有,则不报该药
+                if (antibioticValueWardList.contains(antibioticValueList.get(i))) {
+                    findNum++;
+                }
+            }
+            if (findNum == antibioticValueList.size() - 1) {
+                continue;
+            }
+            dateList = ad.getValue();
+            int matchNum = 0;
+            List<String> wardDateStr = antibioticDateWard.get(drugKey);
+            for (int i = 0; i < dateList.size() - 1; i++) {
+                start = dateList.get(i);        //抗生素开医嘱时间
+                change = dateList.get(i + 1);   //抗生素用量改变时间
+                Date adStart = StringUtil.parseDateTime(start);
+                Date adChange = StringUtil.parseDateTime(change);
+                for (int j = 0; j < wardDateStr.size() - 1; j++) {
+                    wardStartStr = wardDateStr.get(j);         //查房记录开抗生素时间
+                    wardChangeStr = wardDateStr.get(j + 1);    //查房记录改变抗生素用量时间
+                    Date wardStart = StringUtil.parseDateTime(wardStartStr);
+                    Date wardChange = StringUtil.parseDateTime(wardChangeStr);
+                    if (!CatalogueUtil.compareTime(adStart, wardStart, 48 * 60L) && !CatalogueUtil.compareTime(adChange, wardChange, 48 * 60L)) {
+                        matchNum++;
+                    }
+                }
+            }
+            if (dateList.size() - 1 != matchNum) {
+                miss.add(drugKey);
+            }
+        }
+
+        if (miss.size() > 0) {
+            status.set("-1");
+            info.set(miss.toString().replaceAll("[\\[\\]]", ""));
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @param antibioticDate   抗生素使用所有时间
+     * @param antibioticStatus 抗生素用量改变状态
+     * @param antibioticValue  抗生素及用量
+     * @param drugName         抗生素名称
+     * @param value            抗生素用量
+     * @param startDateStr     抗生素使用时间(医嘱开始时间或查房时间)
+     */
+    private void collectAntibioticInfo(Map<String, List<String>> antibioticDate, Map<String, Integer> antibioticStatus, Map<String, List<Double>> antibioticValue, String drugName, String value, String startDateStr) {
+        double v = -1;
+        try {
+            v = Double.parseDouble(getNumber(value));
+        } catch (Exception e) {
+            System.out.println("THR03069:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (!antibioticValue.containsKey(drugName)) {
+            antibioticValue.put(drugName, Lists.newArrayList(v));
+            antibioticDate.put(drugName, Lists.newArrayList(startDateStr));
+            antibioticStatus.put(drugName, 0);
+        } else {
+            List<Double> beforeValue = antibioticValue.get(drugName);
+            if (beforeValue.get(beforeValue.size() - 1) < v) {
+                beforeValue.add(v);
+                antibioticValue.put(drugName, beforeValue);//添加该抗生素更大的值
+                antibioticStatus.put(drugName, antibioticStatus.get(drugName) + 1);
+                antibioticDate.get(drugName).add(startDateStr);
+            }
+        }
+    }
+
+    public static String getNumber(String content) {
+        String group = "";
+        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9]|\\.\\d*[1-9]|0)";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            group = matcher.group(0);
+        }
+        return group;
+    }
+
+    /**
+     * 如果文本包含中括号([海正]美罗培南针),取括号之后的文字
+     *
+     * @param str
+     * @return
+     */
+    private String removeBracket(String str) {
+        if (str.contains("]") && str.indexOf("]") != str.length() - 1) {
+            return str.substring(str.indexOf("]") + 1);
+        }
+        return str;
+    }
+
+}

+ 156 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03070.java

@@ -0,0 +1,156 @@
+package com.lantone.qc.kernel.catalogue.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.LisDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author HUJING
+ * @create 2020-08-17 15:29
+ * @desc 异常化验未记录
+ **/
+@Component
+public class THR03070 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<LisDoc> lisDocs = inputInfo.getLisDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (lisDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
+            return;
+        }
+        //异常数据列表
+        List<String> abnormal = Lists.newArrayList();
+        for (LisDoc lisDoc : lisDocs) {
+            double resultValue = -1, max = -1, min = -1;
+            Map<String, String> structureMap = lisDoc.getStructureMap();
+            String itemName = structureMap.get("报告名称");
+            String result = structureMap.get("检验结果");
+            String reference = structureMap.get("参考值");
+            if (StringUtil.isBlank(itemName) || StringUtil.isBlank(result) || StringUtil.isBlank(reference)) {
+                continue;
+            }
+            itemName = itemName.split("=")[1];
+            //1.化验结果是阳性时,直接把该化验名称放入异常数据列表中
+            if (result.contains("阳")) {
+                abnormal.add(itemName);
+                continue;
+            }
+            //2.化验结果为阴性,或化验结果中不包含数字,跳过该条化验结果
+            if (result.contains("阴") || !CatalogueUtil.numberExist(result)) {
+                continue;
+            }
+            try {
+                resultValue = Double.parseDouble(result);
+            } catch (Exception e) {
+                System.out.println("THR03070--解析result出错:" + itemName + "->" + result);
+            }
+            if (resultValue < 0) {
+                continue;
+            }
+            //3.化验正常值在一个范围内,以“-”分割,最小值最大值返回都扩大10%,再与化验结果比较,在这范围之外,把该化验名称放入异常数据列表中
+            if (reference.contains("-")) {
+                try {
+                    String[] minMax = reference.split("-");
+                    min = Double.parseDouble(getNumber(minMax[0])) * 0.85;        //最小值范围缩小10%
+                    String maxNumber = getNumber(minMax[1]);
+                    if (StringUtil.isNotBlank(maxNumber)) {
+                        max = Double.parseDouble(maxNumber) * 1.15;    //最大值范围扩大10%
+                    }
+                    //化验结果在正常范围之外(返回扩大10%),把该化验名称放入异常数据列表中
+                    if (min >= 0 && max >= 0 && (resultValue < min || resultValue > max)) {
+                        abnormal.add(itemName);
+                    }
+                } catch (Exception e) {
+                    System.out.println("THR03070--3.出异常");
+                }
+            }
+            //4.化验正常值比某个值小,但该化验结果比该值大,把该化验名称放入异常数据列表中
+            else if (reference.contains("<")) {
+                try {
+                    String maxNumber = getNumber(reference);
+                    if (StringUtil.isNotBlank(maxNumber)) {
+                        max = Double.parseDouble(maxNumber);
+                    }
+                    if (resultValue > max) {
+                        abnormal.add(itemName);
+                    }
+                } catch (Exception e) {
+                    System.out.println("THR03070--4.出异常");
+                }
+            }
+            //5.化验正常值比某个值大,但该化验结果比该值小,把该化验名称放入异常数据列表中
+            else if (reference.contains(">")) {
+                try {
+                    String maxNumber = getNumber(reference);
+                    if (StringUtil.isNotBlank(maxNumber)) {
+                        min = Double.parseDouble(maxNumber);
+                    }
+                    if (resultValue < min) {
+                        abnormal.add(itemName);
+                    }
+                } catch (Exception e) {
+                    System.out.println("THR03070--5.出异常");
+                }
+            }
+        }
+
+        Map<String, Integer> abnormalCount = Maps.newHashMap();
+        abnormal = abnormal.stream().map(x -> {
+            if (x.contains("[")) {
+                x = x.substring(0, x.indexOf("["));
+            }
+            if (x.contains("(")) {
+                x = x.substring(0, x.indexOf("("));
+            }
+            x = x.replaceAll("[^\\u4e00-\\u9fa5]", "");
+            return x;
+        }).distinct().collect(Collectors.toList());
+        abnormal.forEach(i -> abnormalCount.put(i, 0));
+
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+            Map<String, String> structureMap = doc.getStructureMap();
+            String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList("体检", "病情记录"));
+            for (String lis : abnormal) {
+                if (content.contains(lis)) {
+                    abnormalCount.put(lis, abnormalCount.get(lis) + 1);
+                }
+            }
+        }
+
+        List<String> abnormalMiss = Lists.newArrayList();
+        for (Map.Entry<String, Integer> lis : abnormalCount.entrySet()) {
+            if (lis.getValue() == 0) {
+                abnormalMiss.add(lis.getKey());
+            }
+        }
+        if (abnormalMiss.size() > 0) {
+            status.set("-1");
+            info.set(abnormalMiss.toString().replaceAll("[\\[\\]]", ""));
+        }
+    }
+
+    public String getNumber(String content) {
+        String group = "";
+        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9]|0)";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            group = matcher.group(0);
+        }
+        return group;
+    }
+}

+ 110 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03071.java

@@ -0,0 +1,110 @@
+package com.lantone.qc.kernel.catalogue.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.FirstCourseRecordDoc;
+import com.lantone.qc.pub.model.doc.LeaveHospitalDoc;
+import com.lantone.qc.pub.model.doc.PacsDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author HUJING
+ * @create 2020-08-17 15:29
+ * @desc 异常检查未记录
+ **/
+@Component
+public class THR03071 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<PacsDoc> pacsDocs = inputInfo.getPacsDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (pacsDocs.size() == 0) {
+            return;
+        }
+        //辅检数据
+        Map<String, List<String>> pacsMap = Maps.newHashMap();
+        Map<String, Integer> pacsCount = Maps.newHashMap();
+        String splitRegex = "[;;]";
+        for (PacsDoc pacsDoc : pacsDocs) {
+            Map<String, String> structureMap = pacsDoc.getStructureMap();
+            String itemName = structureMap.get("报告名称");
+            String itemDiag = structureMap.get("检查结果诊断");
+            if (StringUtil.isBlank(itemName) || StringUtil.isBlank(itemDiag)) {
+                continue;
+            }
+            if (itemName.contains("检查")) {
+                itemName = itemName.substring(0, itemName.indexOf("检查") + 2);
+            }
+            String[] itemDiags = itemDiag.split(splitRegex);
+            List<String> itemDiagList = Lists.newArrayList(itemDiags);
+            if (pacsMap.containsKey(itemName)) {
+                pacsMap.get(itemName).addAll(itemDiagList);
+            } else {
+                pacsMap.put(itemName, itemDiagList);
+            }
+        }
+        //检查项目对应数据初始化
+        pacsMap.keySet().stream().forEach(i -> pacsCount.put(i, 0));
+
+        if (threeLevelWardDocs.size() > 0) {
+            for (ThreeLevelWardDoc doc : threeLevelWardDocs.get(0).getAllDoctorWradDocs()) {
+                Map<String, String> structureMap = doc.getStructureMap();
+                String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList("体检", "病情记录"));
+                findPacs(pacsMap, pacsCount, content);
+            }
+        }
+
+        if (firstCourseRecordDoc != null) {
+            Map<String, String> structureMap = firstCourseRecordDoc.getStructureMap();
+            String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList("病例特点"));
+            findPacs(pacsMap, pacsCount, content);
+        }
+
+        if (leaveHospitalDoc != null) {
+            Map<String, String> structureMap = leaveHospitalDoc.getStructureMap();
+            String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList("诊治经过"));
+            findPacs(pacsMap, pacsCount, content);
+        }
+
+        List<String> pacsMiss = Lists.newArrayList();
+        for (Map.Entry<String, Integer> pacs : pacsCount.entrySet()) {
+            if (pacs.getValue() == 0) {
+                pacsMiss.add(pacs.getKey());
+            }
+        }
+
+        if (pacsMiss.size() > 0) {
+            status.set("-1");
+            info.set(pacsMiss.toString().replaceAll("[\\[\\]]", ""));
+        }
+    }
+
+    /**
+     * 从文本中查找辅检对应的诊断信息,只要找到一个能对上,pacsCount对应辅检项目的数量就+1
+     *
+     * @param pacsMap   key:辅检项目  value:List[诊断信息]
+     * @param pacsCount key:辅检项目    value:匹配的数量
+     * @param content   文本
+     */
+    private void findPacs(Map<String, List<String>> pacsMap, Map<String, Integer> pacsCount, String content) {
+        for (Map.Entry<String, List<String>> map : pacsMap.entrySet()) {
+            for (String itemDiag : map.getValue()) {
+                if (content.contains(itemDiag)) {
+                    pacsCount.put(map.getKey(), pacsCount.get(map.getKey()) + 1);
+                    break;
+                }
+            }
+        }
+    }
+}

+ 239 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03072.java

@@ -0,0 +1,239 @@
+package com.lantone.qc.kernel.catalogue.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.kernel.util.SimilarityUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.entity.Drug;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author HUJING
+ * @create 2020-08-21 10:21
+ * @desc 抗生素减用原因不明确
+ **/
+@Component
+public class THR03072 extends QCCatalogue {
+    @Autowired
+    SimilarityUtil similarityUtil;
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
+            return;
+        }
+        //抗生素及开医嘱时间(包括减用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDate = Maps.newHashMap();
+        //抗生素减用集合   key:抗生素名    value:  0:未减用,1及以上:减用次数
+        Map<String, Integer> antibioticStatus = Maps.newHashMap();
+        //抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValue = Maps.newHashMap();
+
+        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
+                .stream()
+                .filter(Objects::nonNull)
+                .map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("抗生素") && StringUtil.isNotBlank(x.get("医嘱单次剂量")))
+                .collect(Collectors.toList());
+
+        docAdvStruct
+                .stream()
+                .map(x -> x.get("医嘱项目名称"))
+                .forEach(y -> antibioticStatus.put(removeBracket(y), 0));
+
+        String drugName = null, value = null, startDateStr = null;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            drugName = removeBracket(drugName);
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugName = drugStandardWord;
+            }
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没减用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+            }
+        }
+        //抗生素减用过的集合如果为空,则一个抗生素都没有减用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //查房记录中抗生素及查房时间(包括减用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateWard = Maps.newHashMap();
+        //查房记录中抗生素减用集合   key:抗生素名    value:  0:未减用,1及以上:减用次数
+        Map<String, Integer> antibioticStatusWard = Maps.newHashMap();
+        //查房记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueWard = Maps.newHashMap();
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+            if (doc.getThreeLevelWardLabel().size() == 0) {
+                continue;
+            }
+            ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+            List<Drug> drugs = label.getDrugs();
+            for (Drug drug : drugs) {
+                String wardDrug = drug.getName();
+                wardDrug = removeBracket(wardDrug);
+                String drugStandardWord = similarityUtil.getDrugStandardWord(wardDrug);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    wardDrug = drugStandardWord;
+                }
+                //药品用量和使用原因都有时
+                if (drug.getConsumption() != null) {
+                    //查房记录抗生素减用过的集合中没包含该抗生素,则认为该抗生素是第一次出现,此时不需要减用原因
+                    if (!antibioticStatusWard.containsKey(wardDrug) || drug.getUsageWardRound() != null) {
+                        String consumption = drug.getConsumption().getName();
+                        collectAntibioticInfo(antibioticDateWard, antibioticStatusWard, antibioticValueWard, wardDrug, consumption, doc.getStructureMap().get("查房日期"));
+                    }
+                }
+            }
+        }
+        //把查房记录中没减用过的抗生素删除
+        /*for (Map.Entry<String, Integer> as : antibioticStatusWard.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDateWard.remove(as.getKey());
+            }
+        }*/
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有减用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有已减用并且该抗生素有减用原因的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素减用时的时间往后两天内,查房记录中该抗生素减用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中减用过的抗生素,查房记录中没出现过,则该抗生素会报出来(存入miss,规则最后会把所有不符合的抗生素都报出来)
+         */
+        List<String> miss = Lists.newArrayList();
+        String drugKey = null, start = null, change = null, wardStartStr = null, wardChangeStr = null;
+        List<String> dateList = null;
+        for (Map.Entry<String, List<String>> ad : antibioticDate.entrySet()) {
+            drugKey = ad.getKey();
+            if (!antibioticDateWard.containsKey(drugKey)) {
+                miss.add(drugKey);
+                continue;
+            }
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);
+            List<Double> antibioticValueWardList = antibioticValueWard.get(drugKey);
+            int findNum = 0;
+            for (int i = 1; i < antibioticValueList.size(); i++) {//从减用的值开始,如果减用过的值查房记录中都有,则不报该药
+                if (antibioticValueWardList.contains(antibioticValueList.get(i))) {
+                    findNum++;
+                }
+            }
+            if (findNum == antibioticValueList.size() - 1) {
+                continue;
+            }
+            dateList = ad.getValue();
+            int matchNum = 0;
+            List<String> wardDateStr = antibioticDateWard.get(drugKey);
+            for (int i = 0; i < dateList.size() - 1; i++) {
+                start = dateList.get(i);        //抗生素开医嘱时间
+                change = dateList.get(i + 1);   //抗生素用量改变时间
+                Date adStart = StringUtil.parseDateTime(start);
+                Date adChange = StringUtil.parseDateTime(change);
+                for (int j = 0; j < wardDateStr.size() - 1; j++) {
+                    wardStartStr = wardDateStr.get(j);         //查房记录开抗生素时间
+                    wardChangeStr = wardDateStr.get(j + 1);    //查房记录改变抗生素用量时间
+                    Date wardStart = StringUtil.parseDateTime(wardStartStr);
+                    Date wardChange = StringUtil.parseDateTime(wardChangeStr);
+                    if (!CatalogueUtil.compareTime(adStart, wardStart, 48 * 60L) && !CatalogueUtil.compareTime(adChange, wardChange, 48 * 60L)) {
+                        matchNum++;
+                    }
+                }
+            }
+            if (dateList.size() - 1 != matchNum) {
+                miss.add(drugKey);
+            }
+        }
+
+        if (miss.size() > 0) {
+            status.set("-1");
+            info.set(miss.toString().replaceAll("[\\[\\]]", ""));
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @param antibioticDate   抗生素使用所有时间
+     * @param antibioticStatus 抗生素用量改变状态
+     * @param antibioticValue  抗生素及用量
+     * @param drugName         抗生素名称
+     * @param value            抗生素用量
+     * @param startDateStr     抗生素使用时间(医嘱开始时间或查房时间)
+     */
+    private void collectAntibioticInfo(Map<String, List<String>> antibioticDate, Map<String, Integer> antibioticStatus, Map<String, List<Double>> antibioticValue, String drugName, String value, String startDateStr) {
+        double v = -1;
+        try {
+            v = Double.parseDouble(getNumber(value));
+        } catch (Exception e) {
+            System.out.println("THR03072:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (!antibioticValue.containsKey(drugName)) {
+            antibioticValue.put(drugName, Lists.newArrayList(v));
+            antibioticDate.put(drugName, Lists.newArrayList(startDateStr));
+            antibioticStatus.put(drugName, 0);
+        } else {
+            List<Double> beforeValue = antibioticValue.get(drugName);
+            if (beforeValue.get(beforeValue.size() - 1) > v) {
+                beforeValue.add(v);
+                antibioticValue.put(drugName, beforeValue);//添减该抗生素更小的值
+                antibioticStatus.put(drugName, antibioticStatus.get(drugName) + 1);
+                antibioticDate.get(drugName).add(startDateStr);
+            }
+        }
+    }
+
+    public static String getNumber(String content) {
+        String group = "";
+        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9]|\\.\\d*[1-9]|0)";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            group = matcher.group(0);
+        }
+        return group;
+    }
+
+    /**
+     * 如果文本包含中括号([海正]美罗培南针),取括号之后的文字
+     *
+     * @param str
+     * @return
+     */
+    private String removeBracket(String str) {
+        if (str.contains("]") && str.indexOf("]") != str.length() - 1) {
+            return str.substring(str.indexOf("]") + 1);
+        }
+        return str;
+    }
+
+}

+ 235 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03074.java

@@ -0,0 +1,235 @@
+package com.lantone.qc.kernel.catalogue.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.kernel.util.SimilarityUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.entity.Drug;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author HUJING
+ * @create 2020-08-18 19:13
+ * @desc 加用抗生素未记录
+ **/
+@Component
+public class THR03074 extends QCCatalogue {
+    @Autowired
+    SimilarityUtil similarityUtil;
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
+            return;
+        }
+        //抗生素及开医嘱时间(包括加用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDate = Maps.newHashMap();
+        //抗生素加用集合   key:抗生素名    value:  0:未加用,1及以上:加用次数
+        Map<String, Integer> antibioticStatus = Maps.newHashMap();
+        //抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValue = Maps.newHashMap();
+
+        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
+                .stream()
+                .filter(Objects::nonNull)
+                .map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("抗生素") && StringUtil.isNotBlank(x.get("医嘱单次剂量")))
+                .collect(Collectors.toList());
+
+        docAdvStruct
+                .stream()
+                .map(x -> x.get("医嘱项目名称"))
+                .forEach(y -> antibioticStatus.put(removeBracket(y), 0));
+
+        String drugName = null, value = null, startDateStr = null;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            drugName = removeBracket(drugName);
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugName = drugStandardWord;
+            }
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没加用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+            }
+        }
+        //抗生素加用过的集合如果为空,则一个抗生素都没有加用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //查房记录中抗生素及查房时间(包括加用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateWard = Maps.newHashMap();
+        //查房记录中抗生素加用集合   key:抗生素名    value:  0:未加用,1及以上:加用次数
+        Map<String, Integer> antibioticStatusWard = Maps.newHashMap();
+        //查房记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueWard = Maps.newHashMap();
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+            if (doc.getThreeLevelWardLabel().size() == 0) {
+                continue;
+            }
+            ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+            List<Drug> drugs = label.getDrugs();
+            for (Drug drug : drugs) {
+                String wardDrug = drug.getName();
+                wardDrug = removeBracket(wardDrug);
+                String drugStandardWord = similarityUtil.getDrugStandardWord(wardDrug);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    wardDrug = drugStandardWord;
+                }
+                if (drug.getConsumption() != null) {
+                    String consumption = drug.getConsumption().getName();
+                    collectAntibioticInfo(antibioticDateWard, antibioticStatusWard, antibioticValueWard, wardDrug, consumption, doc.getStructureMap().get("查房日期"));
+                }
+            }
+        }
+        //把查房记录中没加用过的抗生素删除
+        /*for (Map.Entry<String, Integer> as : antibioticStatusWard.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDateWard.remove(as.getKey());
+            }
+        }*/
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有加用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有加用时的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素加用时的时间往后两天内,查房记录中该抗生素加用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中加用过的抗生素,查房记录中没出现过,则该抗生素会报出来(存入miss,规则最后会把所有不符合的抗生素都报出来)
+         */
+        List<String> miss = Lists.newArrayList();
+        String drugKey = null, start = null, change = null, wardStartStr = null, wardChangeStr = null;
+        List<String> dateList = null;
+        for (Map.Entry<String, List<String>> ad : antibioticDate.entrySet()) {
+            drugKey = ad.getKey();
+            if (!antibioticDateWard.containsKey(drugKey)) {
+                miss.add(drugKey);
+                continue;
+            }
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);
+            List<Double> antibioticValueWardList = antibioticValueWard.get(drugKey);
+            int findNum = 0;
+            for (int i = 1; i < antibioticValueList.size(); i++) {//从加用的值开始,如果加用过的值查房记录中都有,则不报该药
+                if (antibioticValueWardList.contains(antibioticValueList.get(i))) {
+                    findNum++;
+                }
+            }
+            if (findNum == antibioticValueList.size() - 1) {
+                continue;
+            }
+            dateList = ad.getValue();
+            int matchNum = 0;
+            List<String> wardDateStr = antibioticDateWard.get(drugKey);
+            for (int i = 0; i < dateList.size() - 1; i++) {
+                start = dateList.get(i);        //抗生素开医嘱时间
+                change = dateList.get(i + 1);   //抗生素用量改变时间
+                Date adStart = StringUtil.parseDateTime(start);
+                Date adChange = StringUtil.parseDateTime(change);
+                for (int j = 0; j < wardDateStr.size() - 1; j++) {
+                    wardStartStr = wardDateStr.get(j);         //查房记录开抗生素时间
+                    wardChangeStr = wardDateStr.get(j + 1);    //查房记录改变抗生素用量时间
+                    Date wardStart = StringUtil.parseDateTime(wardStartStr);
+                    Date wardChange = StringUtil.parseDateTime(wardChangeStr);
+                    if (!CatalogueUtil.compareTime(adStart, wardStart, 48 * 60L) && !CatalogueUtil.compareTime(adChange, wardChange, 48 * 60L)) {
+                        matchNum++;
+                    }
+                }
+            }
+            if (dateList.size() - 1 != matchNum) {
+                miss.add(drugKey);
+            }
+        }
+
+        if (miss.size() > 0) {
+            status.set("-1");
+            info.set(miss.toString().replaceAll("[\\[\\]]", ""));
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @param antibioticDate   抗生素使用所有时间
+     * @param antibioticStatus 抗生素用量改变状态
+     * @param antibioticValue  抗生素及用量
+     * @param drugName         抗生素名称
+     * @param value            抗生素用量
+     * @param startDateStr     抗生素使用时间(医嘱开始时间或查房时间)
+     */
+    private void collectAntibioticInfo(Map<String, List<String>> antibioticDate, Map<String, Integer> antibioticStatus, Map<String, List<Double>> antibioticValue, String drugName, String value, String startDateStr) {
+        double v = -1;
+        try {
+            v = Double.parseDouble(getNumber(value));
+        } catch (Exception e) {
+            System.out.println("THR03074:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (!antibioticValue.containsKey(drugName)) {
+            antibioticValue.put(drugName, Lists.newArrayList(v));
+            antibioticDate.put(drugName, Lists.newArrayList(startDateStr));
+            antibioticStatus.put(drugName, 0);
+        } else {
+            List<Double> beforeValue = antibioticValue.get(drugName);
+            if (beforeValue.get(beforeValue.size() - 1) < v) {
+                beforeValue.add(v);
+                antibioticValue.put(drugName, beforeValue);//添加该抗生素更大的值
+                antibioticStatus.put(drugName, antibioticStatus.get(drugName) + 1);
+                antibioticDate.get(drugName).add(startDateStr);
+            }
+        }
+    }
+
+    public static String getNumber(String content) {
+        String group = "";
+        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9]|\\.\\d*[1-9]|0)";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            group = matcher.group(0);
+        }
+        return group;
+    }
+
+    /**
+     * 如果文本包含中括号([海正]美罗培南针),取括号之后的文字
+     *
+     * @param str
+     * @return
+     */
+    private String removeBracket(String str) {
+        if (str.contains("]") && str.indexOf("]") != str.length() - 1) {
+            return str.substring(str.indexOf("]") + 1);
+        }
+        return str;
+    }
+
+}

+ 226 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/threelevelward/THR03075.java

@@ -0,0 +1,226 @@
+package com.lantone.qc.kernel.catalogue.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.kernel.util.SimilarityUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.entity.Drug;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author HUJING
+ * @create 2020-08-18 19:13
+ * @desc 减用抗生素未记录
+ **/
+@Component
+public class THR03075 extends QCCatalogue {
+    @Autowired
+    SimilarityUtil similarityUtil;
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (doctorAdviceDocs.size() == 0 || threeLevelWardDocs.size() == 0) {
+            return;
+        }
+        //抗生素及开医嘱时间(包括减用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDate = Maps.newHashMap();
+        //抗生素减用集合   key:抗生素名    value:  0:未减用,1及以上:减用次数
+        Map<String, Integer> antibioticStatus = Maps.newHashMap();
+        //抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValue = Maps.newHashMap();
+
+        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
+                .stream()
+                .filter(Objects::nonNull)
+                .map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("抗生素") && StringUtil.isNotBlank(x.get("医嘱单次剂量")))
+                .collect(Collectors.toList());
+
+        docAdvStruct
+                .stream()
+                .map(x -> x.get("医嘱项目名称"))
+                .forEach(y -> antibioticStatus.put(removeBracket(y), 0));
+
+        String drugName = null, value = null, startDateStr = null;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            drugName = removeBracket(drugName);
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugName = drugStandardWord;
+            }
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没减用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+            }
+        }
+        //抗生素减用过的集合如果为空,则一个抗生素都没有减用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //查房记录中抗生素及查房时间(包括减用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateWard = Maps.newHashMap();
+        //查房记录中抗生素减用集合   key:抗生素名    value:  0:未减用,1及以上:减用次数
+        Map<String, Integer> antibioticStatusWard = Maps.newHashMap();
+        //查房记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueWard = Maps.newHashMap();
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+            if (doc.getThreeLevelWardLabel().size() == 0) {
+                continue;
+            }
+            ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+            List<Drug> drugs = label.getDrugs();
+            for (Drug drug : drugs) {
+                String wardDrug = drug.getName();
+                wardDrug = removeBracket(wardDrug);
+                String drugStandardWord = similarityUtil.getDrugStandardWord(wardDrug);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    wardDrug = drugStandardWord;
+                }
+                if (drug.getConsumption() != null) {
+                    String consumption = drug.getConsumption().getName();
+                    collectAntibioticInfo(antibioticDateWard, antibioticStatusWard, antibioticValueWard, wardDrug, consumption, doc.getStructureMap().get("查房日期"));
+                }
+            }
+        }
+        //把查房记录中没减用过的抗生素删除
+        /*for (Map.Entry<String, Integer> as : antibioticStatusWard.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDateWard.remove(as.getKey());
+            }
+        }*/
+
+        List<String> miss = Lists.newArrayList();
+        String drugKey = null, start = null, change = null, wardStartStr = null, wardChangeStr = null;
+        List<String> dateList = null;
+        for (Map.Entry<String, List<String>> ad : antibioticDate.entrySet()) {
+            drugKey = ad.getKey();
+            if (!antibioticDateWard.containsKey(drugKey)) {
+                miss.add(drugKey);
+                continue;
+            }
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);
+            List<Double> antibioticValueWardList = antibioticValueWard.get(drugKey);
+            int findNum = 0;
+            for (int i = 1; i < antibioticValueList.size(); i++) {//从减用的值开始,如果减用过的值查房记录中都有,则不报该药
+                if (antibioticValueWardList.contains(antibioticValueList.get(i))) {
+                    findNum++;
+                }
+            }
+            if (findNum == antibioticValueList.size() - 1) {
+                continue;
+            }
+            dateList = ad.getValue();
+            int matchNum = 0;
+            List<String> wardDateStr = antibioticDateWard.get(drugKey);
+            for (int i = 0; i < dateList.size() - 1; i++) {
+                start = dateList.get(i);        //抗生素开医嘱时间
+                change = dateList.get(i + 1);   //抗生素用量改变时间
+                Date adStart = StringUtil.parseDateTime(start);
+                Date adChange = StringUtil.parseDateTime(change);
+                for (int j = 0; j < wardDateStr.size() - 1; j++) {
+                    wardStartStr = wardDateStr.get(j);         //查房记录开抗生素时间
+                    wardChangeStr = wardDateStr.get(j + 1);    //查房记录改变抗生素用量时间
+                    Date wardStart = StringUtil.parseDateTime(wardStartStr);
+                    Date wardChange = StringUtil.parseDateTime(wardChangeStr);
+                    if (!CatalogueUtil.compareTime(adStart, wardStart, 48 * 60L) && !CatalogueUtil.compareTime(adChange, wardChange, 48 * 60L)) {
+                        matchNum++;
+                    }
+                }
+            }
+            if (dateList.size() - 1 != matchNum) {
+                miss.add(drugKey);
+            }
+        }
+
+        if (miss.size() > 0) {
+            status.set("-1");
+            info.set(miss.toString().replaceAll("[\\[\\]]", ""));
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @param antibioticDate   抗生素使用所有时间
+     * @param antibioticStatus 抗生素用量改变状态
+     * @param antibioticValue  抗生素及用量
+     * @param drugName         抗生素名称
+     * @param value            抗生素用量
+     * @param startDateStr     抗生素使用时间(医嘱开始时间或查房时间)
+     */
+    private void collectAntibioticInfo(Map<String, List<String>> antibioticDate, Map<String, Integer> antibioticStatus, Map<String, List<Double>> antibioticValue, String drugName, String value, String startDateStr) {
+        double v = -1;
+        try {
+            v = Double.parseDouble(getNumber(value));
+        } catch (Exception e) {
+            System.out.println("THR03075:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (!antibioticValue.containsKey(drugName)) {
+            antibioticValue.put(drugName, Lists.newArrayList(v));
+            antibioticDate.put(drugName, Lists.newArrayList(startDateStr));
+            antibioticStatus.put(drugName, 0);
+        } else {
+            List<Double> beforeValue = antibioticValue.get(drugName);
+            if (beforeValue.get(beforeValue.size() - 1) > v) {
+                beforeValue.add(v);
+                antibioticValue.put(drugName, beforeValue);//添减该抗生素更小的值
+                antibioticStatus.put(drugName, antibioticStatus.get(drugName) + 1);
+                antibioticDate.get(drugName).add(startDateStr);
+            }
+        }
+    }
+
+    public static String getNumber(String content) {
+        String group = "";
+        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9]|\\.\\d*[1-9]|0)";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            group = matcher.group(0);
+        }
+        return group;
+    }
+
+    /**
+     * 如果文本包含中括号([海正]美罗培南针),取括号之后的文字
+     *
+     * @param str
+     * @return
+     */
+    private String removeBracket(String str) {
+        if (str.contains("]") && str.indexOf("]") != str.length() - 1) {
+            return str.substring(str.indexOf("]") + 1);
+        }
+        return str;
+    }
+}

+ 30 - 9
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/ThreeLevelWardAI.java

@@ -2,6 +2,7 @@ package com.lantone.qc.kernel.structure.ai;
 
 import com.alibaba.fastjson.JSONArray;
 import com.alibaba.fastjson.JSONObject;
+import com.google.common.collect.Lists;
 import com.lantone.qc.kernel.client.CRFServiceClient;
 import com.lantone.qc.kernel.structure.ai.process.EntityProcessThreeLevelWard;
 import com.lantone.qc.kernel.util.CatalogueUtil;
@@ -16,13 +17,9 @@ import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
 import com.lantone.qc.pub.util.ListUtil;
 import com.lantone.qc.pub.util.StringUtil;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * @ClassName : ThreeLevelWardAI
@@ -34,7 +31,7 @@ public class ThreeLevelWardAI extends ModelAI {
     /**
      *
      */
-    public static List<String> medicalTextType = Arrays.asList("CourseRecord_cx", "CourseRecordLast_cx");
+    public static List<String> medicalTextType = Arrays.asList("CourseRecord_cx", "CourseRecordLast_cx", "CourseRecordSRR");
     public static String entityRelationObject = "entity_relation_object";
     public static String outputs = "outputs";
     public static String content = "content";
@@ -64,10 +61,18 @@ public class ThreeLevelWardAI extends ModelAI {
             putContent(crfContent, medicalTextType.get(0), lastDirStructureMap.get("病情记录"), Content.director + "最后一次");
         }
 
-        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();//所有查房记录
+        //所有查房记录,2020/08/20新增所有查房记录抽取:实验室检查、辅助检查、药品等
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
         if (allDoctorWradDocs.size() > 0) {
             Map<String, String> lastWardStructureMap = allDoctorWradDocs.get(allDoctorWradDocs.size() - 1).getStructureMap();
             putContent(crfContent, medicalTextType.get(1), lastWardStructureMap.get("病情记录"), "末次查房");
+            //2020/08/20新增所有查房记录抽取:实验室检查、辅助检查、药品等
+            for (int i = 0; i < allDoctorWradDocs.size(); i++) {
+                Map<String, String> structureMap = allDoctorWradDocs.get(i).getStructureMap();
+                String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList("体检", "病情记录", "治疗计划和措施"));
+                putContent(crfContent, medicalTextType.get(2), content, "第" + i + "次查房");
+            }
+
         }
 
         List<Map<String, String>> directorDifficultRecord = new ArrayList<>();
@@ -124,6 +129,14 @@ public class ThreeLevelWardAI extends ModelAI {
             }
         }
 
+        //2020/08/20新增所有查房记录抽取:实验室检查、辅助检查、药品等
+        for (int i = 0; i < allDoctorWradDocs.size(); i++) {
+            if (midData.get("第" + i + "次查房") == null) {
+                continue;
+            }
+            putWardRoundCrfData(midData.getJSONObject("第" + i + "次查房"), inputInfo, 0);//查房记录
+        }
+
     }
 
     protected void putContent(JSONArray crfContent, String medicalTextType, String text, String sign, String wardDate) {
@@ -177,6 +190,14 @@ public class ThreeLevelWardAI extends ModelAI {
                     allDoctorWradDocs.get(allDoctorWradDocs.size() - 1).setThreeLevelWardLabel(lastWardLebelList);
                     break;
             }
+            //2020/08/20新增所有查房记录抽取:实验室检查、辅助检查、药品等
+            Pattern p = Pattern.compile("\\d");
+            Matcher matcher = p.matcher(detailTitle);
+            if (matcher.find()) {
+                String num = matcher.group(0);
+                int n = Integer.parseInt(num);
+                threeLevelWardDoc.getAllDoctorWradDocs().get(n).setThreeLevelWardLabel(Lists.newArrayList(threeLevelWardLabel));
+            }
         } else {
             List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
             for (ThreeLevelWardDoc wardDoc : allDoctorWradDocs) {

+ 29 - 3
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/model/EntityEnum.java

@@ -18,7 +18,7 @@ public enum EntityEnum {
     UNKNOWN("情况不详"), HEALTH("健康情况"), AGE("年龄"), SMOKING_HISTORY("吸烟史"),
     HISTORY_OF_ALCOHOL_INTAKE("饮酒史"), USAGE("用量"), MENSES("月经"), LEUKORRHEA("白带"),
     BIRTH_HIS("生育情况"), CONJUGAL_RELATION("夫妻关系"), RELATIVES("家属"), GROUP_CONSULTATION("会诊"), ORGANISM("生物体"),
-    OCCUPATION("职业"), LOCATION("地点"), DEAD("死亡"), DEAD_REASON("死亡原因"),TUMOUR("肿瘤病史"),
+    OCCUPATION("职业"), LOCATION("地点"), DEAD("死亡"), DEAD_REASON("死亡原因"), TUMOUR("肿瘤病史"),
     SIMILAR_DISEASE_KEYWORD("相似疾病"), GENETIC_DISEASE_KEYWORD("家族遗传病"), EPIDEMIC_AREA_HISTORY("疫区史"), SPECIAL_HOBBY("特殊嗜好"),
     CONTACT_HISTORY("接触史"), MARITAL_HISTORY("冶游史"), MARITAL_STATUS("婚姻情况"), MARRYIAGE("结婚年龄"), NEAR_RELATION("近亲史"),
     CURE_AIM("治疗目的"), OTHER("其他"), OUTERCOURTYARD("外院"), NURSING_LEVEL("护理级别"), CHIEF("主诉"), NOTES("注意事项"),
@@ -27,8 +27,10 @@ public enum EntityEnum {
     TITLE_FOR_DIFF("鉴别诊断标题"), TITLE_FOR_TREAT("诊疗计划标题"), TITLE("标题"), TREATMENT_PLAN("诊疗计划"),
     TITLE_OF_OPERATIVE_FINDINGS("手术经过标题"), TITLE_OF_MEASURES_AFTER_OP("术后处理措施标题"),
     TITLE_OF_RISK_AFTER_OP("术后风险标题"), TITLE_OF_ATTENTION_AFTER_OP("术后注意事项标题"), VITAL("生命体征"),
-    BETTER_FINDING("好转表现"),OUTCOME_TO_BETTER("转归情况-好转"),OUTCOME_CURE("转归情况-治愈"),POSITIVE_FINDING("阳性表现"),
-    INSPECTION_ITEMS("检查项目"),DISCHARGE_MODE("出院方式");
+    BETTER_FINDING("好转表现"), OUTCOME_TO_BETTER("转归情况-好转"), OUTCOME_CURE("转归情况-治愈"), POSITIVE_FINDING("阳性表现"),
+    INSPECTION_ITEMS("检查项目"), DISCHARGE_MODE("出院方式"), LABORATORY_PACKAGE("实验室检查套餐"), LABORATORY_RESULTS("实验室检查结果"),
+    AUXILIARY_EXAMINATION_RESULTS("辅检结果"), CONSUMPTION("用量-查房"), USAGE_WARD_ROUND("用法"), STOP("停用"),
+    REASONS_FOR_ANTIBIOTIC("抗生素使用原因"),CHECK_TIME("检查时间");
 
     private String value;
 
@@ -235,6 +237,30 @@ public enum EntityEnum {
             case "出院方式":
                 entityEnum = EntityEnum.DISCHARGE_MODE;
                 break;
+            case "实验室检查套餐":
+                entityEnum = EntityEnum.LABORATORY_PACKAGE;
+                break;
+            case "实验室检查结果":
+                entityEnum = EntityEnum.LABORATORY_RESULTS;
+                break;
+            case "辅检结果":
+                entityEnum = EntityEnum.AUXILIARY_EXAMINATION_RESULTS;
+                break;
+            case "用量-查房":
+                entityEnum = EntityEnum.CONSUMPTION;
+                break;
+            case "用法":
+                entityEnum = EntityEnum.USAGE_WARD_ROUND;
+                break;
+            case "停用":
+                entityEnum = EntityEnum.STOP;
+                break;
+            case "抗生素使用原因":
+                entityEnum = EntityEnum.REASONS_FOR_ANTIBIOTIC;
+                break;
+            case "检查时间":
+                entityEnum = EntityEnum.CHECK_TIME;
+                break;
         }
         return entityEnum;
     }

+ 68 - 14
kernel/src/main/java/com/lantone/qc/kernel/structure/ai/process/EntityProcessThreeLevelWard.java

@@ -3,21 +3,11 @@ package com.lantone.qc.kernel.structure.ai.process;
 import com.alibaba.fastjson.JSONObject;
 import com.lantone.qc.kernel.structure.ai.model.EntityEnum;
 import com.lantone.qc.kernel.structure.ai.model.Lemma;
-import com.lantone.qc.pub.model.entity.BetterFinding;
-import com.lantone.qc.pub.model.entity.Clinical;
-import com.lantone.qc.pub.model.entity.Diag;
-import com.lantone.qc.pub.model.entity.OutcomeCure;
-import com.lantone.qc.pub.model.entity.OutcomeToBetter;
-import com.lantone.qc.pub.model.entity.PositiveFinding;
-import com.lantone.qc.pub.model.entity.Sign;
-import com.lantone.qc.pub.model.entity.TreatmentPlan;
+import com.lantone.qc.pub.model.entity.*;
 import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import lombok.extern.slf4j.Slf4j;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 
 /**
  * @ClassName : EntityProcessThreeLevelWard
@@ -25,6 +15,7 @@ import java.util.Map;
  * @Author : 胡敬
  * @Date: 2020-03-20 11:20
  */
+@Slf4j
 public class EntityProcessThreeLevelWard extends EntityProcess {
     private List<String> titleList = Arrays.asList(
             EntityEnum.TITLE_FOR_SIGN.toString(),
@@ -56,7 +47,7 @@ public class EntityProcessThreeLevelWard extends EntityProcess {
         }
 
         //诊断文本
-        if (titleText.get(EntityEnum.TITLE_FOR_DIAG.toString()) != null){
+        if (titleText.get(EntityEnum.TITLE_FOR_DIAG.toString()) != null) {
             List<String> diagList = titleText.get(EntityEnum.TITLE_FOR_DIAG.toString());
             String text = textJoin(content, diagList);
             //诊断文本
@@ -167,6 +158,69 @@ public class EntityProcessThreeLevelWard extends EntityProcess {
             positiveFindings.add(positiveFinding);
         }
         threeLevelWardLabel.setPositiveFindings(positiveFindings);
+
+        try {
+            //实验室检查套餐
+            List<LaboratoryPackage> laboratoryPackages = new ArrayList<>();
+            List<Lemma> laboratoryPackageLemmas = createEntityTree(aiOut, EntityEnum.LABORATORY_PACKAGE.toString());
+            for (Lemma lemma : laboratoryPackageLemmas) {
+                LaboratoryPackage laboratoryPackage = new LaboratoryPackage();
+                laboratoryPackage.setName(lemma.getText());
+                laboratoryPackage.setCheckTime(findT(lemma, new CheckTime(), EntityEnum.CHECK_TIME.toString()));
+                laboratoryPackages.add(laboratoryPackage);
+            }
+            threeLevelWardLabel.setLaboratoryPackages(laboratoryPackages);
+
+            //实验室检查
+            List<Laboratory> laboratories = new ArrayList<>();
+            List<Lemma> laboratoryLemmas = createEntityTree(aiOut, EntityEnum.LABORATORY.toString());
+            for (Lemma lemma : laboratoryLemmas) {
+                Laboratory laboratory = new Laboratory();
+                laboratory.setName(lemma.getText());
+                for (Lemma relationLemma : lemma.getRelationLemmas()) {
+                    if (relationLemma.getProperty().equals(EntityEnum.LABORATORY_PACKAGE.toString())) {
+                        LaboratoryPackage laboratoryPackage = new LaboratoryPackage();
+                        laboratoryPackage.setCheckTime(findT(lemma, new CheckTime(), EntityEnum.CHECK_TIME.toString()));
+                        laboratory.setLaboratoryPackage(findT(lemma, laboratoryPackage, EntityEnum.LABORATORY_PACKAGE.toString()));//实验室检查套餐
+                    }
+                }
+                laboratory.setCheckTime(findT(lemma, new CheckTime(), EntityEnum.CHECK_TIME.toString()));//检查时间
+                laboratory.setLaboratoryResults(findT(lemma, new LaboratoryResults(), EntityEnum.LABORATORY_RESULTS.toString()));//实验室检查结果
+                laboratories.add(laboratory);
+            }
+            threeLevelWardLabel.setLaboratories(laboratories);
+
+            //辅助检查
+            List<AuxiliaryExamination> auxiliaryExaminations = new ArrayList<>();
+            List<Lemma> auxiliaryExaminationLemmas = createEntityTree(aiOut, EntityEnum.AUXILIARY_EXAMINATION.toString());
+            for (Lemma lemma : auxiliaryExaminationLemmas) {
+                AuxiliaryExamination auxiliaryExamination = new AuxiliaryExamination();
+                auxiliaryExamination.setName(lemma.getText());
+                auxiliaryExamination.setCheckTime(findT(lemma, new CheckTime(), EntityEnum.CHECK_TIME.toString()));
+                auxiliaryExamination.setAuxiliaryExaminationResults(findT(lemma, new AuxiliaryExaminationResults(), EntityEnum.AUXILIARY_EXAMINATION_RESULTS.toString()));
+                auxiliaryExaminations.add(auxiliaryExamination);
+            }
+            threeLevelWardLabel.setAuxiliaryExaminations(auxiliaryExaminations);
+
+            //药物
+            List<Drug> drugs = new ArrayList<>();
+            List<Lemma> DrugLemmas = createEntityTree(aiOut, EntityEnum.DRUG.toString());
+            for (Lemma lemma : DrugLemmas) {
+                Drug drug = new Drug();
+                drug.setName(lemma.getText());
+                drug.setConsumption(findT(lemma, new Consumption(), EntityEnum.CONSUMPTION.toString().split("-")[0]));
+                drug.setUsageWardRound(findT(lemma, new UsageWardRound(), EntityEnum.USAGE_WARD_ROUND.toString()));
+                drug.setFrequency(findT(lemma, new Frequency(), EntityEnum.FREQUENCY.toString()));
+                drug.setStop(findT(lemma, new Stop(), EntityEnum.STOP.toString()));
+                drug.setReasonsForAntibiotic(findT(lemma, new ReasonsForAntibiotic(), EntityEnum.REASONS_FOR_ANTIBIOTIC.toString()));
+                drugs.add(drug);
+            }
+            threeLevelWardLabel.setDrugs(drugs);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+            log.error(e.getMessage(), e);
+        }
         return threeLevelWardLabel;
     }
 

+ 1 - 1
kernel/src/main/java/com/lantone/qc/kernel/util/CatalogueUtil.java

@@ -43,7 +43,7 @@ public class CatalogueUtil {
         if (StringUtil.isEmpty(content)) {
             return "";
         }
-        return content.replaceAll("[\r\n|/r/n|\n|/n|/t|\t]", " ").trim();
+        return content.replaceAll("[\r\n|\n|\t]", " ").trim();
     }
 
     public static String[] extractDigit(String value) {

+ 80 - 0
kernel/src/main/java/com/lantone/qc/kernel/util/SimilarityUtil.java

@@ -0,0 +1,80 @@
+package com.lantone.qc.kernel.util;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.lantone.qc.kernel.client.SimilarityServiceClient;
+import com.lantone.qc.pub.model.vo.SimilarityVo;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description:
+ * @author: rengb
+ * @time: 2020/8/24 10:06
+ */
+@Slf4j
+@Component
+public class SimilarityUtil {
+
+    @Autowired
+    private SimilarityServiceClient similarityServiceClient;
+
+    /**
+     * 根据相似度,获取药品标准词
+     *
+     * @param drugWord
+     * @return
+     */
+    public String getDrugStandardWord(String drugWord) {
+        String ret = null;
+        try {
+            JSONArray standardWords = similarityRequest("drug", drugWord, 1);
+            if (standardWords != null && standardWords.size() == 1) {
+                JSONObject standardWord = standardWords.getJSONObject(0);
+                if (standardWord.getDoubleValue("rate") > 0.8) {
+                    ret = standardWord.getString("standard_word");
+                }
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return ret;
+    }
+
+    /**
+     * 发送相似度请求
+     *
+     * @param word_type 由服务方定的词的类型,取值:疾病—disease、症状—symptom、手术和操作—operation、药品—drug、实验室检查-lis、辅助检查-pacs、体征-vital
+     * @param word      原始词
+     * @param number    最多返回个数,如果不是有效的值,默认返回1个,最多返回10个
+     * @return 返回具体数据集合
+     */
+    public JSONArray similarityRequest(String word_type, String word, int number) {
+        if (StringUtil.isBlank(word_type) || StringUtil.isBlank(word) || number == 0) {
+            return null;
+        }
+        JSONArray ret = null;
+        try {
+            JSONArray similarContent = new JSONArray();
+            JSONObject detailContent = new JSONObject();
+            detailContent.put("word_type", word_type);
+            detailContent.put("word", word);
+            detailContent.put("number", number);
+            similarContent.add(detailContent);
+            SimilarityVo similarityVo = new SimilarityVo();
+            similarityVo.setData(similarContent);
+            String similarJson = similarityServiceClient.getAnnotation(similarityVo);
+            JSONObject similarJsonObject = JSON.parseObject(similarJson);
+            if (similarJsonObject.getBooleanValue("status")) {
+                ret = similarJsonObject.getJSONArray("standard_words");
+            }
+        } catch (Exception e) {
+            log.error(e.getMessage(), e);
+        }
+        return ret;
+    }
+
+}

+ 17 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/AuxiliaryExamination.java

@@ -0,0 +1,17 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : AuxiliaryExamination
+ * @Description : 辅助检查
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class AuxiliaryExamination extends General {
+    private CheckTime checkTime;
+    private AuxiliaryExaminationResults auxiliaryExaminationResults;
+}

+ 15 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/AuxiliaryExaminationResults.java

@@ -0,0 +1,15 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : AuxiliaryExamination
+ * @Description : 辅助检查结果
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class AuxiliaryExaminationResults extends General {
+}

+ 15 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/CheckTime.java

@@ -0,0 +1,15 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : CheckTime
+ * @Description : 检查时间
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class CheckTime extends General {
+}

+ 16 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/Consumption.java

@@ -0,0 +1,16 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : Consumption
+ * @Description : 药物用量
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class Consumption extends General {
+    
+}

+ 20 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/Drug.java

@@ -0,0 +1,20 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : Drug
+ * @Description : 药物名称
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class Drug extends General {
+    private Consumption consumption;        //用量
+    private UsageWardRound usageWardRound;  //用法
+    private Frequency frequency;            //频率
+    private Stop stop;                      //停用
+    private ReasonsForAntibiotic reasonsForAntibiotic;  //抗生素使用原因
+}

+ 18 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/Laboratory.java

@@ -0,0 +1,18 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : Laboratory
+ * @Description : 实验室检查
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class Laboratory extends General {
+    private LaboratoryPackage laboratoryPackage;
+    private CheckTime checkTime;
+    private LaboratoryResults laboratoryResults;
+}

+ 16 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/LaboratoryPackage.java

@@ -0,0 +1,16 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : LaboratoryResults
+ * @Description : 实验室检查套餐
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Getter
+@Setter
+public class LaboratoryPackage extends General {
+    private CheckTime checkTime;
+}

+ 13 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/LaboratoryResults.java

@@ -0,0 +1,13 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : LaboratoryResults
+ * @Description : 实验室检查结果
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+public class LaboratoryResults extends General {
+}

+ 16 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/ReasonsForAntibiotic.java

@@ -0,0 +1,16 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : ReasonsForAntibiotic
+ * @Description : 抗生素使用原因
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class ReasonsForAntibiotic extends General {
+    
+}

+ 16 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/Stop.java

@@ -0,0 +1,16 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : Stop
+ * @Description : 停用
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class Stop extends General {
+    
+}

+ 15 - 0
public/src/main/java/com/lantone/qc/pub/model/entity/UsageWardRound.java

@@ -0,0 +1,15 @@
+package com.lantone.qc.pub.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * @ClassName : UsageWardRound
+ * @Description : 用法
+ * @Author : 胡敬
+ * @Date: 2020-08-20 10:09
+ */
+@Setter
+@Getter
+public class UsageWardRound extends General {
+}

+ 20 - 23
public/src/main/java/com/lantone/qc/pub/model/label/ThreeLevelWardLabel.java

@@ -1,13 +1,6 @@
 package com.lantone.qc.pub.model.label;
 
-import com.lantone.qc.pub.model.entity.BetterFinding;
-import com.lantone.qc.pub.model.entity.Clinical;
-import com.lantone.qc.pub.model.entity.Diag;
-import com.lantone.qc.pub.model.entity.OutcomeCure;
-import com.lantone.qc.pub.model.entity.OutcomeToBetter;
-import com.lantone.qc.pub.model.entity.PositiveFinding;
-import com.lantone.qc.pub.model.entity.Sign;
-import com.lantone.qc.pub.model.entity.TreatmentPlan;
+import com.lantone.qc.pub.model.entity.*;
 import lombok.Getter;
 import lombok.Setter;
 
@@ -23,19 +16,23 @@ import java.util.List;
 @Getter
 @Setter
 public class ThreeLevelWardLabel {
-    int serious = 0;    //0:不严重患者,1:危重患者,2:疑难患者,3:抢救患者
-    String title;   //医师职称
-    List<Clinical> clinicals = new ArrayList<>();   //病史(临床表现)
-    List<Sign> signs = new ArrayList<>();   //查体
-    List<Diag> diffDiag = new ArrayList<>();   //鉴别诊断
-    String diagBasisText;   //诊断依据文本
-    String diffDiagText;    //鉴别诊断文本
-    String diagText;    //诊断文本
-    List<Diag> diags = new ArrayList<>();   //补充诊断/初步诊断/修正诊断
-    String dischargeMode;  //离院方式
-    List<TreatmentPlan> treatmentPlans = new ArrayList<>();   //诊疗计划
-    List<BetterFinding> betterFindings = new ArrayList<>();   //好转表现
-    List<OutcomeToBetter> outcomeToBetters = new ArrayList<>();   //转归情况-好转
-    List<OutcomeCure> outcomeCures = new ArrayList<>();   //转归情况-治愈
-    List<PositiveFinding> positiveFindings = new ArrayList<>();   //阳性表现
+    private int serious = 0;    //0:不严重患者,1:危重患者,2:疑难患者,3:抢救患者
+    private String title;   //医师职称
+    private List<Clinical> clinicals = new ArrayList<>();   //病史(临床表现)
+    private List<Sign> signs = new ArrayList<>();   //查体
+    private List<Diag> diffDiag = new ArrayList<>();   //鉴别诊断
+    private String diagBasisText;   //诊断依据文本
+    private String diffDiagText;    //鉴别诊断文本
+    private String diagText;    //诊断文本
+    private List<Diag> diags = new ArrayList<>();   //补充诊断/初步诊断/修正诊断
+    private String dischargeMode;  //离院方式
+    private List<TreatmentPlan> treatmentPlans = new ArrayList<>();   //诊疗计划
+    private List<BetterFinding> betterFindings = new ArrayList<>();   //好转表现
+    private List<OutcomeToBetter> outcomeToBetters = new ArrayList<>();   //转归情况-好转
+    private List<OutcomeCure> outcomeCures = new ArrayList<>();   //转归情况-治愈
+    private List<PositiveFinding> positiveFindings = new ArrayList<>();   //阳性表现
+    private List<LaboratoryPackage> laboratoryPackages = new ArrayList<>();  //实验室检查套餐
+    private List<Laboratory> laboratories = new ArrayList<>();  //实验室检查
+    private List<AuxiliaryExamination> auxiliaryExaminations = new ArrayList<>();  //辅助检查
+    private List<Drug> drugs = new ArrayList<>();  //药物
 }