瀏覽代碼

宁海妇幼和一院合入、其他一院更新合入4

rengb 4 年之前
父節點
當前提交
fe153dca27
共有 100 個文件被更改,包括 12946 次插入0 次删除
  1. 56 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH0024.java
  2. 76 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH0032.java
  3. 41 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH03093.java
  4. 42 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH03095.java
  5. 195 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/crisisvaluereport/CRI0382.java
  6. 41 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/firstcourserecord/FIRC03094.java
  7. 95 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/firstpagerecord/FIRP03104.java
  8. 92 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/firstpagerecord/FIRP03105.java
  9. 90 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03096.java
  10. 85 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03097.java
  11. 79 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03098.java
  12. 91 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03099.java
  13. 79 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03100.java
  14. 73 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03101.java
  15. 79 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03102.java
  16. 129 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03103.java
  17. 80 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03106.java
  18. 200 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE0369.java
  19. 173 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/preoperativediscussion/PRE0328.java
  20. 99 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR0134.java
  21. 125 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR0139.java
  22. 348 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR03076.java
  23. 101 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR03090.java
  24. 235 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0011.java
  25. 43 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0023.java
  26. 56 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0024.java
  27. 70 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0025.java
  28. 60 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0026.java
  29. 54 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0028.java
  30. 72 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0029.java
  31. 56 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0030.java
  32. 69 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0031.java
  33. 63 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0042.java
  34. 70 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0043.java
  35. 53 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0047.java
  36. 65 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0053.java
  37. 109 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0058.java
  38. 260 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH02980.java
  39. 84 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0372.java
  40. 110 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0375.java
  41. 109 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0376.java
  42. 95 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0378.java
  43. 97 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0397.java
  44. 47 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0439.java
  45. 55 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0454.java
  46. 68 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/consultation/CON0281.java
  47. 106 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/consultation/CON0526.java
  48. 117 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/crisisvaluereport/CRI0382.java
  49. 212 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/dutyshiftsystem/DUT0296.java
  50. 91 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/dutyshiftsystem/DUT0598.java
  51. 201 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/firstcourserecord/FIRC0095.java
  52. 40 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/firstpagerecord/FIRP02972.java
  53. 63 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/leavehospital/LEA02901.java
  54. 82 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/leavehospital/LEA02987.java
  55. 130 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02891.java
  56. 130 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02892.java
  57. 130 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02897.java
  58. 130 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02898.java
  59. 91 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02930.java
  60. 177 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE0369.java
  61. 48 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE0628.java
  62. 54 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE0630.java
  63. 123 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/preoperativediscussion/PRE03064.java
  64. 136 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/preoperativediscussion/PRE0328.java
  65. 188 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0125.java
  66. 222 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0126.java
  67. 229 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0128.java
  68. 116 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0144.java
  69. 138 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02900.java
  70. 69 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02931.java
  71. 508 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02985.java
  72. 506 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02986.java
  73. 156 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03023.java
  74. 148 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03024.java
  75. 325 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03044.java
  76. 300 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03069.java
  77. 300 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03072.java
  78. 296 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03074.java
  79. 298 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03075.java
  80. 598 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03077.java
  81. 434 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03079.java
  82. 91 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03090.java
  83. 136 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0434.java
  84. 136 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0435.java
  85. 142 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0436.java
  86. 138 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0588.java
  87. 143 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0601.java
  88. 111 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0602.java
  89. 56 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0024.java
  90. 70 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0025.java
  91. 60 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0026.java
  92. 54 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0028.java
  93. 72 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0029.java
  94. 56 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0030.java
  95. 69 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0031.java
  96. 63 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0042.java
  97. 70 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0043.java
  98. 53 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0047.java
  99. 65 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0053.java
  100. 0 0
      kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0058.java

+ 56 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH0024.java

@@ -0,0 +1,56 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.behospitalized;
+
+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.entity.Wound;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 外伤史未填写
+ * @author: rengb
+ * @time: 2020/3/10 13:53
+ */
+@Component
+public class BEH0024 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> behStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructureMap.get("手术外伤史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        List<Wound> wounds = pastLabel.getWounds();
+        if (ListUtil.isNotEmpty(wounds)) {
+            if (wounds.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+        //硬规则匹配
+        String pastLabelText = pastLabel.getText();
+        if (pastLabelText.contains("外伤") || pastLabelText.contains("详见原病历") || pastLabelText.contains("骨折")
+                || pastLabelText.contains("右跟骨手术")) {
+            status.set("0");
+        }
+    }
+}

+ 76 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH0032.java

@@ -0,0 +1,76 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.behospitalized;
+
+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.entity.Diag;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 疾病名未标引号
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0032 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        if (pastLabel == null) {
+            return;
+        }
+        List<Diag> diags = pastLabel.getDiags();
+        if (ListUtil.isEmpty(diags)) {
+            return;
+        }
+        List<String> wtDiagNames = CatalogueUtil.noInQuotes(
+                diags.stream().filter(diag -> diag.getNegative() == null).map(i -> i.getHospitalDiagName()).filter(i -> StringUtil.isNotEmpty(i)).distinct().collect(Collectors.toList()),
+                pastLabel.getText().replace("“", "\"").replace("”", "\"")
+        );
+        String pastText = pastLabel.getText();
+        List<String> wtDiagList = new ArrayList<>();
+        if (ListUtil.isNotEmpty(wtDiagNames)) {
+            for (String wtDiagName : wtDiagNames) {
+                int index = pastText.indexOf(wtDiagName);
+                if (index - 1 > 0) {
+                    String markText = pastText.substring(index - 1, index);
+                    if (markText.contains("\"") || markText.contains("“")) {
+                        continue;
+                    }
+                }
+                if (index + wtDiagName.length() + 1 < pastText.length() && index > 0) {
+                    String markText = pastText.substring(index + wtDiagName.length(), index + wtDiagName.length() + 1);
+                    if (markText.contains("\"") || markText.contains("”")) {
+                        continue;
+                    }
+                }
+                //由于crf模型将 40年前摔伤致腰背部这段文字解析成(腰背部)疾病,故在此加条件过滤该问题
+                if(!"摔伤致腰背部".contains(wtDiagName))
+                {
+                    wtDiagList.add(wtDiagName);
+                }
+            }
+        }
+
+        if (ListUtil.isNotEmpty(wtDiagList)) {
+            status.set("-1");
+            for (String wtDiag : wtDiagList) {
+                info.set(info.get() + wtDiag + " ");
+            }
+        }
+    }
+
+}

+ 41 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH03093.java

@@ -0,0 +1,41 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.behospitalized;
+
+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.VTEGradeDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @ClassName : BEH03095
+ * @Description : 入院记录的VTE评分时间早于VTE评分表生成时间
+ * @Author : sxl
+ * @Date: 2021-03-10 10:08
+ */
+@Component
+public class BEH03093 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //1.判断是否有VTE评分
+        VTEGradeDoc vteGradeDoc = inputInfo.getVteGradeDoc();
+        if(vteGradeDoc!= null&& StringUtil.isNotBlank(vteGradeDoc.getStructureMap().get("评估日期")))
+        {
+            //2.获取入院记录时间
+            BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+            if(beHospitalizedDoc!=null&&StringUtil.isNotBlank(beHospitalizedDoc.getStructureMap().get("入院日期")))
+            {
+                String beHospitalizeTime =  beHospitalizedDoc.getStructureMap().get("入院日期");
+                //3.获取vte评分时间
+                String vteGradeTime = vteGradeDoc.getStructureMap().get("评估日期");
+                if(CatalogueUtil.compareDate(beHospitalizeTime, vteGradeTime,1))
+                {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+}

+ 42 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/behospitalized/BEH03095.java

@@ -0,0 +1,42 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.behospitalized;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.ADLGradeDoc;
+import com.lantone.qc.pub.model.doc.FirstCourseRecordDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @ClassName : BEH03095
+ * @Description : ADL评分未在病程记录之前完成
+ * @Author : sxl
+ * @Date: 2021-03-10 10:08
+ */
+@Component
+public class BEH03095 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //1.判断是否有adl评分
+        ADLGradeDoc adlGradeDoc = inputInfo.getAdlGradeDoc();
+        if(adlGradeDoc!= null&& StringUtil.isNotBlank(adlGradeDoc.getStructureMap().get(Content.adl_grade_time)))
+        {
+            //2.获取首次病程记录时间
+            FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+            if(firstCourseRecordDoc!=null&&StringUtil.isNotBlank(firstCourseRecordDoc.getStructureMap().get("病历日期")))
+            {
+                String firstCourseRecorTime =  firstCourseRecordDoc.getStructureMap().get("病历日期");
+                //3.获取adl评分时间
+                String adlGradeTime = adlGradeDoc.getStructureMap().get(Content.adl_grade_time);
+                if(CatalogueUtil.compareDate(firstCourseRecorTime, adlGradeTime,1))
+                {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+}

+ 195 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/crisisvaluereport/CRI0382.java

@@ -0,0 +1,195 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.crisisvaluereport;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.SimilarityServiceClient;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.CrisisInfoDoc;
+import com.lantone.qc.pub.model.doc.CrisisValueReportDoc;
+import com.lantone.qc.pub.model.entity.Annotation;
+import com.lantone.qc.pub.model.vo.SimilarityVo;
+import com.lantone.qc.pub.util.StringUtil;
+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.Date;
+import java.util.List;
+
+/**
+ * @Description: 接到危急值报告后6小时内无病程记录
+ * @author: rengb
+ * @time: 2020/3/19 19:54
+ */
+@Component
+public class CRI0382 extends QCCatalogue {
+    @Autowired
+    SimilarityServiceClient similarityServiceClient;
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //无危急值结构化信息
+        List<CrisisInfoDoc> crisisInfoDocs = inputInfo.getCrisisInfoDocs();
+        if (crisisInfoDocs == null || crisisInfoDocs.size() == 0) {
+            return;
+        }
+        //有结构化信息但无危急值文书,如果报告时间都未超过6小时 允许无文书
+        boolean isOutTime = false;
+        Date currentDate = new Date();
+        int timeCha = 21600000;
+        String[] dateFormats = new String[]{"yyyy年MM月dd日HH时mm分", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm"};
+        for (CrisisInfoDoc crisisInfoDoc : crisisInfoDocs) {
+            String reptTime = crisisInfoDoc.getStructureMap().get("报告时间");
+            if (StringUtils.isNotEmpty(reptTime)) {
+                if (currentDate.getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime() > timeCha) {
+                    isOutTime = true;
+                }
+            }
+        }
+        if (isOutTime == false) {   //所有文书都未超过6小时,规则直接通过
+            return;
+        }
+
+        List<CrisisValueReportDoc> crisisValueReportDocs = inputInfo.getCrisisValueReportDocs();
+        //如果有危急值结构化数据 但无文书则直接提醒无危急值报告
+        if (crisisValueReportDocs == null || crisisValueReportDocs.size() == 0) {
+            status.set("-1");
+            return;
+        }
+        List<String> findCrisises = new ArrayList<>();
+        List<String> allCrisises = new ArrayList<>();
+        crisisInfoDocs.forEach(crisisInfoDoc -> {
+            String reptTime = crisisInfoDoc.getStructureMap().get("报告时间");
+            String crisisName = crisisInfoDoc.getStructureMap().get("危急结果值");
+            String crisisNm = "";
+            String companyNum = "";
+            if (crisisName.contains("项目为")) {
+                crisisName = crisisName.substring(crisisName.indexOf("项目为") + 3);
+            }
+            if (crisisName.contains(",结果:")) {
+                crisisNm = crisisName.split(",结果:")[0];
+                companyNum = crisisName.split(",结果:")[1];
+                if (StringUtil.isNotBlank(companyNum) && companyNum.contains("单位:")) {
+                    companyNum = companyNum.replaceAll("单位:", "");
+                }
+            }
+            allCrisises.add(reptTime);
+            if (StringUtils.isNotEmpty(reptTime)) {
+                //当前时间和报告时间未超过6小时,规则通过
+                if (currentDate.getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime() < timeCha) {
+                    findCrisises.add(reptTime);
+                } else {
+                    for (CrisisValueReportDoc crisisValueReportDoc : crisisValueReportDocs) {
+                        String recordTimeStr = crisisValueReportDoc.getStructureMap().get("病历日期");
+                        String docReptContent = crisisValueReportDoc.getStructureMap().get("病情分析及处理");
+                        //超声:添加文本相似度去比较病情分析及处理
+                        boolean flag = getLikeRate(submitContent(StringUtil.removeBlank(docReptContent))
+                                                        ,StringUtil.removeBlank(crisisName));
+                        if ((StringUtil.parseDateTime(recordTimeStr, dateFormats).getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime()) < timeCha
+                                && (StringUtil.removeBlank(docReptContent).contains(StringUtil.removeBlank(crisisName)) ||
+                                (StringUtil.isNotBlank(crisisNm) && StringUtil.isNotBlank(companyNum)
+                                        && StringUtil.removeBlank(docReptContent).contains(StringUtil.removeBlank(crisisNm))
+                                        && StringUtil.removeBlank(docReptContent).contains(StringUtil.removeBlank(companyNum))
+                                ))
+                            ||flag) {
+                            findCrisises.add(reptTime);
+                            break;
+                        }
+                    }
+                }
+            }
+        });
+        // (StringUtil.parseDateTime(recordTimeStr, dateFormats).getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime()) > 0
+        //                                &&
+        if (findCrisises.size() != allCrisises.size()) {
+            status.set("-1");
+            allCrisises.forEach(reptTime -> {
+                String criticalValueName = "";
+                if (!findCrisises.contains(reptTime) && !info.get().contains(reptTime)) {
+                    for (CrisisInfoDoc crisisInfoDoc : crisisInfoDocs) {
+                        if (reptTime.equals(crisisInfoDoc.getStructureMap().get("报告时间"))) {
+                            criticalValueName = crisisInfoDoc.getStructureMap().get("危急值名称");
+                        }
+                    }
+                    if (StringUtils.isEmpty(info.get())) {
+                        info.set(reptTime + " 危急值名称(" + criticalValueName + ")");
+                    } else {
+                        info.set(info.get() + "; " + reptTime + " 危急值名称(" + criticalValueName + ")");
+                    }
+                }
+            });
+        }
+    }
+    /**
+     * @Author songxl
+     * @Description 内容截取
+     * @Date  2021/3/29
+     * @Param [content]
+     * @Return java.lang.String
+     * @MethodName submitContent
+     */
+    private String submitContent(String content) {
+        if(StringUtil.isNotBlank(content))
+        {
+            if(content.contains("具体为"))
+            {
+                content = content.split("具体为")[1];
+            }
+            if(content.contains("处理意见"))
+            {
+                content = content.split("处理意见")[0];
+            }
+        }
+        return content;
+
+    }
+
+    /**
+     * @Author songxl
+     * @Description 获取文本相似度
+     * @Date  2021/3/29
+     * @Param [text_1, text_2]
+     * @Return boolean
+     * @MethodName getFlag
+     */
+    private boolean getLikeRate(String text_1, String text_2) {
+        if(text_1.contains("超声"))
+        {
+            JSONArray similarContent = new JSONArray();
+            JSONObject detailContent = new JSONObject();
+            detailContent.put("text_1", text_1);
+            detailContent.put("text_2", text_2);
+            similarContent.add(detailContent);
+            //存储CRF完整所需结构数据
+            SimilarityVo similarityVo = new SimilarityVo();
+            similarityVo.setData(similarContent);
+            //获取CRF模型返回数据
+            JSONArray data = getAnnotation(similarityServiceClient, similarityVo).getData();
+            double likeRate = 0d;
+            if (data.size() > 0) {
+                JSONObject dataContent = data.getJSONObject(0);
+                likeRate = dataContent.getDoubleValue("like_rate");
+            }
+            if(likeRate>0.8)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+    private Annotation getAnnotation(SimilarityServiceClient similarityServiceClient, SimilarityVo similarityVo) {
+        Annotation annotation = new Annotation();
+        try {
+            String annotation_str = similarityServiceClient.getAnnotation(similarityVo);
+            annotation = JSON.parseObject(annotation_str, Annotation.class);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+        } finally {
+            return annotation;
+        }
+    }
+}

+ 41 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/firstcourserecord/FIRC03094.java

@@ -0,0 +1,41 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.firstcourserecord;
+
+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.VTEGradeDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @ClassName : FIRC03094
+ * @Description : 首次病程记录的VTE评分时间早于VTE评分表生成时间
+ * @Author : sxl
+ * @Date: 2021-03-10 10:08
+ */
+@Component
+public class FIRC03094 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //1.判断是否有VTE评分
+        VTEGradeDoc vteGradeDoc = inputInfo.getVteGradeDoc();
+        if(vteGradeDoc!= null&& StringUtil.isNotBlank(vteGradeDoc.getStructureMap().get("评估日期")))
+        {
+            //2.获取首次病程记录时间
+            FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+            if(firstCourseRecordDoc!=null&&StringUtil.isNotBlank(firstCourseRecordDoc.getStructureMap().get("病历日期")))
+            {
+                String firstCourseRecorTime =  firstCourseRecordDoc.getStructureMap().get("病历日期");
+                //3.获取vte评分时间
+                String vteGradeTime = vteGradeDoc.getStructureMap().get("评估日期");
+                if(CatalogueUtil.compareDate(firstCourseRecorTime, vteGradeTime,1))
+                {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+}

+ 95 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/firstpagerecord/FIRP03104.java

@@ -0,0 +1,95 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.firstpagerecord;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.JsonObject;
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.FirstPageRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.regex.Pattern;
+
+
+/**
+ * @ClassName: FIRP03104
+ * @Description: 麻醉医师与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class FIRP03104 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录和麻醉记录麻醉医师 判断麻醉医师是否相同
+         */
+        status.set("0");
+        //1.获取患者的病案首页相关文档,手麻记录
+        FirstPageRecordDoc firstPageRecordDoc = inputInfo.getFirstPageRecordDoc();
+        if(firstPageRecordDoc==null||firstPageRecordDoc.getStructureExtMap().isEmpty()
+            ||firstPageRecordDoc.getStructureExtMap().get("手术信息")==null
+        ||(JSONArray.parseArray(""+firstPageRecordDoc.getStructureExtMap().get("手术信息"))).isEmpty())//无病案首页相关记录或者病案首页无手术记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.获取手术信息
+       try{
+           JSONArray  operationArr = JSONArray.parseArray(""+firstPageRecordDoc.getStructureExtMap().get("手术信息"));
+           for(Object operationOBJ:operationArr)
+           {
+               JSONObject operationJson = JSONObject.parseObject(operationOBJ+"");
+                  if(operationJson!=null)
+                  {
+                      //2.1获取手术麻醉医师
+                      String anesthesiaDoctor = operationJson.getString("麻醉医师");
+                      //通过手术时间判断是否是同一个手术
+                      Date operationStartTime = DateUtil.newParseDateTime(operationJson.getString("手术日期"));
+                      if(StringUtil.isBlank(anesthesiaDoctor)) {
+                          status.set("-1");
+                          return;
+                      }
+                      if(operationStartTime ==null) {continue;}
+                      anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                          //2.2遍历手麻记录,对比手术名称(如果手麻开始时间和病案首页记录时间在3个小时以内则判定为同一台手术)
+                          if (CatalogueUtil.compareTime(operationStartTime,
+                                  anesthesiaRecordDoc.getAnesStartTime(),
+                                  Long.valueOf(2 * 60)))
+                          {
+                              if(StringUtil.isNotBlank(anesthesiaRecordDoc.getAnesthesiaDoctor()))
+                              {
+                                  if(!StringUtil.equals(anesthesiaDoctor,anesthesiaRecordDoc.getAnesthesiaDoctor()))
+                                  {
+                                      status.set("-1");
+                                      return;
+                                  }
+                              }
+                          }
+
+                      });
+                  }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 92 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/firstpagerecord/FIRP03105.java

@@ -0,0 +1,92 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.firstpagerecord;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.FirstPageRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: FIRP03105
+ * @Description: 麻醉方式与麻醉记录不一致(病案首页)
+ * @Author songxl
+ * @Date 2021/3/22
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class FIRP03105 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录和麻醉记录麻醉方式 判断麻醉方式是否相同
+         */
+        status.set("0");
+        //1.获取患者的病案首页相关文档,手麻记录
+        FirstPageRecordDoc firstPageRecordDoc = inputInfo.getFirstPageRecordDoc();
+        if(firstPageRecordDoc==null||firstPageRecordDoc.getStructureExtMap().isEmpty()
+                ||firstPageRecordDoc.getStructureExtMap().get("手术信息")==null
+                ||(JSONArray.parseArray(""+firstPageRecordDoc.getStructureExtMap().get("手术信息"))).isEmpty())//无病案首页相关记录或者病案首页无手术记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.获取手术信息
+        try{
+            JSONArray  operationArr = JSONArray.parseArray(""+firstPageRecordDoc.getStructureExtMap().get("手术信息"));
+            for(Object operationOBJ:operationArr)
+            {
+                JSONObject operationJson = JSONObject.parseObject(operationOBJ+"");
+                if(operationJson!=null)
+                {
+                    //2.1获取手术麻醉方式
+                    String anesthesiaMethod = operationJson.getString("麻醉方式");
+                    //通过手术时间判断是否是同一个手术
+                    Date operationStartTime = DateUtil.newParseDateTime(operationJson.getString("手术日期"));
+                    if(StringUtil.isBlank(anesthesiaMethod)) {
+                        status.set("-1");
+                        return;
+                    }
+                    if(operationStartTime ==null) {continue;}
+                    anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                        //2.2遍历手麻记录,对比手术名称(如果手麻开始时间和病案首页记录时间在3个小时以内则判定为同一台手术)
+                        if (CatalogueUtil.compareTime(operationStartTime,
+                                anesthesiaRecordDoc.getAnesStartTime(),
+                                Long.valueOf(2 * 60)))
+                        {
+                            if(StringUtil.isNotBlank(anesthesiaRecordDoc.getAnesthesiaMethod()))
+                            {
+                                if(!StringUtil.equals(anesthesiaMethod,anesthesiaRecordDoc.getAnesthesiaMethod()))
+                                {
+                                    status.set("-1");
+                                    return;
+                                }
+                            }
+                        }
+
+                    });
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            log.error(e.getMessage(),e);
+        }
+
+    }
+}

+ 90 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03096.java

@@ -0,0 +1,90 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+import com.alibaba.fastjson.JSONArray;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.SimilarityServiceClient;
+import com.lantone.qc.kernel.structure.ai.ModelAI;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03096
+ * @Description: 术后诊断与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03096 extends QCCatalogue {
+    @Autowired
+    SimilarityServiceClient similarityServiceClient;
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录的术后诊断和麻醉记录的术后诊断进行相似度对比
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术记录术后诊断、手术时间
+                   String diagAfterOperation = operationDoc.getOperationRecordDoc().getStructureMap().get("术中后诊断");
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                                    .getStructureMap().get("手术开始时间"));
+                   if(operationStartTime==null || StringUtil.isBlank(diagAfterOperation)) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       //2.2遍历手麻记录,找到该手术时间的手麻记录
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null)
+                       {
+                           if(DateUtils.isSameInstant(operationStartTime,anesthesiaRecordDoc.getAnesStartTime()))
+                           {
+                               String anesDiagAfterOperation = anesthesiaRecordDoc.getDiagAfterOperation();
+                               //相似度对比
+                               /* 文本相似度模型 */
+                               ModelAI modelAI = new ModelAI();
+                               JSONArray similarContent = new JSONArray();
+                               modelAI.putContent(similarContent,diagAfterOperation,anesDiagAfterOperation);
+                               double likeRate = modelAI.loadSimilarAI(similarContent, similarityServiceClient);
+                               if (likeRate < 0.9) {
+                                   status.set("-1");
+                                   return;
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 85 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03097.java

@@ -0,0 +1,85 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+import com.alibaba.fastjson.JSONArray;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.ChiefPresentSimilarityServiceClient;
+import com.lantone.qc.kernel.structure.ai.ModelAI;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03097
+ * @Description: 手术开始时间与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03097 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录和麻醉记录手术开始时间 判断时间是否相同
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术时间
+                   Date operationEndTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术结束时间"));
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(operationStartTime==null||operationEndTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       if(anesthesiaRecordDoc.getAnesEndTime()!=null) {
+                           if (DateUtils.isSameInstant(operationEndTime, anesthesiaRecordDoc.getAnesEndTime())) {
+                               //2.2遍历手麻记录,对比手术时间开始时间
+                               if(anesthesiaRecordDoc.getAnesStartTime()!=null)
+                               {
+                                   if(!DateUtils.isSameInstant(operationStartTime,anesthesiaRecordDoc.getAnesStartTime()))
+                                   {
+                                       status.set("-1");
+                                       return;
+                                   }
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 79 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03098.java

@@ -0,0 +1,79 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03098
+ * @Description: 手术名称与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03098 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录和麻醉记录手术名称 判断手术名称是否相同
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术名称
+                   String operationName = operationDoc.getOperationRecordDoc().getStructureMap().get("手术名称");
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(StringUtil.isBlank(operationName)||operationStartTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null) {
+                           if (DateUtils.isSameInstant(operationStartTime, anesthesiaRecordDoc.getAnesStartTime())) {
+                               //2.2遍历手麻记录,对比手术名称
+                               if(StringUtil.isNotBlank(anesthesiaRecordDoc.getOperationName()))
+                               {
+                                   if(!StringUtil.equals(operationName,anesthesiaRecordDoc.getOperationName()))
+                                   {
+                                       status.set("-1");
+                                       return;
+                                   }
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 91 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03099.java

@@ -0,0 +1,91 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+import com.alibaba.fastjson.JSONArray;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.SimilarityServiceClient;
+import com.lantone.qc.kernel.structure.ai.ModelAI;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03099
+ * @Description: 术前诊断与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03099 extends QCCatalogue {
+    @Autowired
+    SimilarityServiceClient similarityServiceClient;
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录的术后诊断和麻醉记录的术前诊断进行相似度对比
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术记录术前诊断、手术时间
+                   String diagBeforeOperation = operationDoc.getOperationRecordDoc().getStructureMap().get("术前诊断");
+                   Date operationStartTime = DateUtil.parseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"),"yyyy年MM月dd日HH时mm分");
+                   if(operationStartTime==null || StringUtil.isBlank(diagBeforeOperation)) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       //2.2遍历手麻记录,找到该手术时间的手麻记录
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null)
+                       {
+                           if(DateUtils.isSameInstant(operationStartTime,anesthesiaRecordDoc.getAnesStartTime()))
+                           {
+                               String anesDiagBeforeOperation = anesthesiaRecordDoc.getDiagBeforeOperation();
+                               //相似度对比
+                               /* 文本相似度模型 */
+                               ModelAI modelAI = new ModelAI();
+                               JSONArray similarContent = new JSONArray();
+                               modelAI.putContent(similarContent,diagBeforeOperation,anesDiagBeforeOperation);
+                               double likeRate = modelAI.loadSimilarAI(similarContent, similarityServiceClient);
+                               if (likeRate < 0.9) {
+                                   status.set("-1");
+                                   return;
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 79 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03100.java

@@ -0,0 +1,79 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03100
+ * @Description: 麻醉医师与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03100 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录的麻醉医师和麻醉记录判断麻醉医师是否相同
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术名称
+                   String anesthesiaDoctor = operationDoc.getOperationRecordDoc().getStructureMap().get("麻醉医师");
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(StringUtil.isBlank(anesthesiaDoctor)||operationStartTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null) {
+                           if (DateUtils.isSameInstant(operationStartTime, anesthesiaRecordDoc.getAnesStartTime())) {
+                               //2.2遍历手麻记录,对比麻醉医师
+                               if(StringUtil.isNotBlank(anesthesiaRecordDoc.getAnesthesiaDoctor()))
+                               {
+                                   if(!StringUtil.equals(anesthesiaDoctor,anesthesiaRecordDoc.getAnesthesiaDoctor()))
+                                   {
+                                       status.set("-1");
+                                       return;
+                                   }
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 73 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03101.java

@@ -0,0 +1,73 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03101
+ * @Description: 出血量与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/19
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03101 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录的出血量和麻醉记录出血量比较
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术时间
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(operationStartTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       //2.2遍历手麻记录,对比手术时间开始时间
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null)
+                       {
+                           if(!DateUtils.isSameInstant(operationStartTime,anesthesiaRecordDoc.getAnesStartTime()))
+                           {
+                               status.set("-1");
+                               return;
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 79 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03102.java

@@ -0,0 +1,79 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03102
+ * @Description: 麻醉方式与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/22
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03102 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录和麻醉记录麻醉方式 判断麻醉方式是否相同
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术麻醉方式
+                   String anesthesiaDoctor = operationDoc.getOperationRecordDoc().getStructureMap().get("麻醉方式");
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(StringUtil.isBlank(anesthesiaDoctor)||operationStartTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null) {
+                           if (DateUtils.isSameInstant(operationStartTime, anesthesiaRecordDoc.getAnesStartTime())) {
+                               //2.2遍历手麻记录,对比麻醉方式
+                               if(StringUtil.isNotBlank(anesthesiaRecordDoc.getAnesthesiaMethod()))
+                               {
+                                   if(!StringUtil.equals(anesthesiaDoctor,anesthesiaRecordDoc.getAnesthesiaMethod()))
+                                   {
+                                       status.set("-1");
+                                       return;
+                                   }
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 129 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03103.java

@@ -0,0 +1,129 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.FirstPageRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03103
+ * @Description: 主刀医师与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/22
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03103 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 1.先去判断手术记录和麻醉记录的主刀医师,如果不一致就直接抛出错误
+         * 2.手术记录和麻醉记录的主刀医师一致,再去和病案首页主刀医师进行比对不一致抛出错误
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术主刀医师
+                   String doctorName = operationDoc.getOperationRecordDoc().getStructureMap().get("主刀医师");
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(StringUtil.isBlank(doctorName)||operationStartTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null) {
+                           if (DateUtils.isSameInstant(operationStartTime, anesthesiaRecordDoc.getAnesStartTime())) {
+                               //2.2遍历手麻记录,对比主刀医师
+                               if (StringUtil.isNotBlank(anesthesiaRecordDoc.getDoctorName())) {
+                                   if (!StringUtil.equals(doctorName, anesthesiaRecordDoc.getDoctorName())) {
+                                       status.set("-1");
+                                       return;
+                                   }
+                                   //2.3 手术记录和麻醉记录如果一致判断是否和病案首页是否一致
+                                   //2.3.1.获取患者的病案首页相关文档,手麻记录
+                                   FirstPageRecordDoc firstPageRecordDoc = inputInfo.getFirstPageRecordDoc();
+                                   if(firstPageRecordDoc==null||firstPageRecordDoc.getStructureExtMap().isEmpty()
+                                           ||firstPageRecordDoc.getStructureExtMap().get("手术信息")==null
+                                           ||(JSONArray.parseArray(""+firstPageRecordDoc.getStructureExtMap().get("手术信息"))).isEmpty())//无病案首页相关记录或者病案首页无手术记录
+                                   {
+                                       return;
+                                   }
+                                   else {
+                                       //2.3.2.获取手术信息
+                                       try {
+                                           JSONArray operationArr = JSONArray.parseArray("" + firstPageRecordDoc.getStructureExtMap().get("手术信息"));
+                                           for (Object operationOBJ : operationArr) {
+                                               JSONObject operationJson = JSONObject.parseObject(operationOBJ + "");
+                                               if (operationJson != null) {
+                                                   //2.1获取手术麻醉方式
+                                                   Long doctorID = operationJson.getLong("手术医生ID");
+                                                   //通过手术时间判断是否是同一个手术
+                                                   Date firOperationStartTime = DateUtil.newParseDateTime(operationJson.getString("手术日期"));
+                                                   if (doctorID == null) {
+                                                       status.set("-1");
+                                                       return;
+                                                   }
+                                                   if (operationStartTime == null) {
+                                                       continue;
+                                                   }
+
+                                                   //2.2遍历手麻记录,对比手术名称(如果手麻开始时间和病案首页记录时间在3个小时以内则判定为同一台手术)
+                                                   if (CatalogueUtil.compareTime(firOperationStartTime,
+                                                           anesthesiaRecordDoc.getAnesStartTime(),
+                                                           Long.valueOf(2 * 60)))
+                                                   {
+                                                       if(!doctorID.equals(anesthesiaRecordDoc.getDid()))
+                                                       {
+                                                           status.set("-1");
+                                                           return;
+                                                       }
+                                                   }
+                                               }
+                                           }
+                                       }
+                                       catch (Exception e)
+                                       {
+                                           log.error(e.getMessage(),e);
+                                       }
+                                   }
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 80 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE03106.java

@@ -0,0 +1,80 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+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.AnesthesiaRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+
+
+/**
+ * @ClassName: OPE03103
+ * @Description: 手术结束时间与麻醉记录不一致
+ * @Author songxl
+ * @Date 2021/3/22
+ * @Version 1.0
+ */
+@Component
+@Slf4j
+public class OPE03106 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 获取手术记录和麻醉记录手术结束时间 判断手术结束时间是否相同
+         */
+        status.set("0");
+        //1.获取患者的手术相关文档,手麻记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs==null||operationDocs.isEmpty())//无手术相关记录
+        {
+            return;
+        }
+        List<AnesthesiaRecordDoc> anesthesiaDocs  = inputInfo.getAnesthesiaRecordDocs();
+        if(anesthesiaDocs==null|| anesthesiaDocs.isEmpty())//无手麻相关记录
+        {
+            return;
+        }
+        //2.遍历获取手术记录
+       try{
+           for(OperationDoc operationDoc:operationDocs)
+           {
+               if(operationDoc.getOperationRecordDoc()!=null)
+               {
+                   //2.1获取手术主刀医师
+                   Date operationEndTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术结束时间"));
+                   Date operationStartTime = DateUtil.newParseDateTime(operationDoc.getOperationRecordDoc()
+                           .getStructureMap().get("手术开始时间"));
+                   if(operationEndTime==null||operationStartTime==null) {continue;}
+                   anesthesiaDocs.forEach(anesthesiaRecordDoc -> {
+                       if(anesthesiaRecordDoc.getAnesStartTime()!=null) {
+                           if (DateUtils.isSameInstant(operationStartTime, anesthesiaRecordDoc.getAnesStartTime())) {
+                               //2.2遍历手麻记录,对比手术结束时间
+                               if(anesthesiaRecordDoc.getAnesStartTime()!=null)
+                               {
+                                   if(!DateUtils.isSameInstant(operationEndTime,anesthesiaRecordDoc.getAnesEndTime()))
+                                   {
+                                       status.set("-1");
+                                       return;
+                                   }
+                               }
+                           }
+                       }
+                   });
+               }
+           }
+       }
+       catch (Exception e)
+       {
+           log.error(e.getMessage(),e);
+       }
+
+    }
+}

+ 200 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/operationdiscussion/OPE0369.java

@@ -0,0 +1,200 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.operationdiscussion;
+
+import com.alibaba.druid.support.json.JSONUtils;
+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.FirstPageRecordDoc;
+import com.lantone.qc.pub.model.doc.MedicalRecordInfoDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.*;
+
+/**
+ * @Description: 主刀医师术前查房未在术前24小时内完成
+ * @author: WANGSY
+ * @time: 2020/11/11 11:22
+ */
+@Component
+public class OPE0369 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        if (outputInfo.getResult() != null) {
+            //1.获取查房记录
+            Map<String, Object> thr03090 = outputInfo.getResult().get("THR03090");
+            //2.如果查房记录不为空且大于零且状态是-1(查房记录报错)
+            if (thr03090 != null && thr03090.size() > 0 && thr03090.get("status").equals("-1")) {
+                return;
+            }
+        }
+
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        String admisTime = "";
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime)) {
+                return;
+            }
+        }
+        FirstPageRecordDoc firstPageRecordDoc = inputInfo.getFirstPageRecordDoc();
+        System.out.println(JSONUtils.toJSONString(firstPageRecordDoc));
+        String behospitalWay = firstPageRecordDoc.getStructureMap().get("入院途径");
+        //先判断是否有手术记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            return;
+        }
+        //北仑传入急诊标志,在首次病程录中
+        if (inputInfo.getFirstCourseRecordDoc() != null) {
+            Map<String, String> structureMap = inputInfo.getFirstCourseRecordDoc().getStructureMap();
+            String title = structureMap.get("标题");
+            if (StringUtil.isNotBlank(title) && title.contains("急诊")) {
+                return;
+            }
+        }
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        int operationCount = getOperationSum(operationDocs); // 获取手术记录次数
+//        long operationCount = operationDocs.stream().filter(operationDoc -> operationDoc.getOperationRecordDoc() != null).count();
+        //主刀查房次数
+        long operateCount = 0;
+        if (operationCount > 0) {
+            //存在手术记录无查房记录
+            if (ListUtil.isEmpty(threeLevelWardDocs)) {
+                status.set("-1");
+                return;
+            }
+            String operationStartDate = "";
+            List<Date> operDateList = new ArrayList<>(); //手术时间
+            for (OperationDoc operationDoc : operationDocs) {
+                if (operationDoc.getOperationRecordDoc() != null) {
+                    Map<String, String> operationDocStructureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+                    operationStartDate = operationDocStructureMap.get("手术开始时间");
+                    if (operationStartDate.contains("年月日")) {
+                        continue;
+                    }
+                    if (StringUtil.isNotBlank(operationStartDate)) {
+                        if (!CatalogueUtil.compareTime(
+                                StringUtil.parseDateTime(admisTime),
+                                StringUtil.parseDateTime(operationStartDate),
+                                Long.valueOf(30))) {
+                            continue;
+                        }
+
+                        //急诊手术处理【入院途径:急诊,且第一次手术时间和入院时间相差1小时】
+                        if(StringUtil.isNotBlank(behospitalWay)&&"急诊".equals(behospitalWay)
+                        &&!CatalogueUtil.compareTime(
+                                StringUtil.parseDateTime(admisTime),
+                                StringUtil.parseDateTime(operationStartDate),
+                                Long.valueOf(60)))
+                        {
+                            continue;
+                        }
+                        operDateList.add(StringUtil.parseDateTime(operationStartDate));
+                    } else {//取不到手术时间
+                        return;
+                    }
+                }
+            }
+
+            if (operDateList.size() > 1) {
+                for (int i = 0; i < operDateList.size(); i++) {
+                    if (i + 1 < operDateList.size()) {
+                        if (!CatalogueUtil.compareTime(operDateList.get(i), operDateList.get(i + 1),
+                                Long.valueOf(24 * 60))) {//如果手术记录是同一天,需有一次术前主刀查房
+                            operationCount--;
+                        }
+                    }
+                }
+            }
+
+            ThreeLevelWardDoc threeLevelWardDoc = threeLevelWardDocs.get(0);
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+                Map<String, String> structureMap = threeLevelWard.getStructureMap();
+                String makeTitle = structureMap.get("查房标题");
+                String writTitle = structureMap.get("文书标题");
+                String recordDateStr = structureMap.get("查房日期");
+                if (operDateList.size() > 0) {
+                    for (Date date : operDateList) {
+                        if (StringUtil.isNotBlank(recordDateStr) && date != null
+                                && StringUtil.parseDateTime(recordDateStr).before(date)) {
+                            if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(recordDateStr), date, Long.valueOf(24 * 60))
+                                    && (((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("主刀")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("主刀")))
+                                    || ((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("术前")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("术前"))))) {
+                                operateCount++;
+                            }
+                        }
+                    }
+                } else {//手术开始时间跟入院时间 小于30分钟,规则不判断
+                    return;
+                }
+            }
+        }
+
+        if (operationCount > 0 && operationCount > operateCount) {
+            status.set("-1");
+            return;
+        }
+    }
+
+
+    /**
+     * 获取手术记录次数
+     *
+     * @param operationDocs
+     * @return
+     */
+    private int getOperationSum(List<OperationDoc> operationDocs) {
+        List<Map<String, Date>> operationDateList = new ArrayList<>();
+        Map<String, Date> operationDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            //获取手术记录对象
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            //获取手术记录文档
+            Map<String, String> structureMap = operationRecordDoc.getStructureMap();
+            String operationStartDateStr = structureMap.get("手术开始时间");
+            if (StringUtil.isNotBlank(operationStartDateStr) && !operationStartDateStr.contains("年月日")) {
+                Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                operationStartDate = DateUtil.dateZeroClear(operationStartDate);
+                if (operationStartDate != null) {
+                    /* 放第一个手术记录的日期到operationDateList */
+                    if (operationDateList.size() == 0) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术开始时间", operationStartDate);
+                        operationDateList.add(operationDate);
+                        continue;
+                    }
+                    /* 如果其中一个手术记录的开始时间到结束时间之间还包含另一个手术,就不往operationDateList里加 */
+                    boolean findInnerOperation = false;
+                    for (Map<String, Date> date : operationDateList) {
+                        Date listStartDate = DateUtil.dateZeroClear(date.get("手术开始时间"));
+                        if (listStartDate.equals(operationStartDate)) {
+                            findInnerOperation = true;
+                            break;
+                        }
+                    }
+                    if (!findInnerOperation) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术开始时间", operationStartDate);
+                        operationDateList.add(operationDate);
+                    }
+                }
+            }
+        }
+        return operationDateList.size();
+    }
+}

+ 173 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/preoperativediscussion/PRE0328.java

@@ -0,0 +1,173 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.preoperativediscussion;
+
+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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.FirstPageRecordDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.FastJsonUtils;
+import com.lantone.qc.pub.util.StringUtil;
+import com.lantone.qc.trans.beilun.BeiLunFirstPageRecordDocTrans;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.*;
+
+/**
+ * @Description: 手术患者无术前讨论记录
+ * @author: rengb
+ * @time: 2020/3/23 15:09
+ */
+@Component
+public class PRE0328 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        /**
+         * 1:如果急诊手术【入院时间-手术开始时间小于30分钟】则可不用写术前小结。
+         * 2:如果入院时间-手术开始时间小于30分钟,则术前讨论、术前小结次数+1。
+         * 3:如果手术记录次数(第一次手术的日期内有其他手术不算次数) 大于 术前讨论、术前小结次数,则出错
+         * 4:患者从抢救/急救室转手术室,到手术开始时间不超过30分钟不用写术前讨论
+         * 5、判断是否是急诊手术;[判断入院途径:急诊、入院时间和手术时间接近这种情况不用术前讨论]
+         * 6、获取急诊手术申请标识,从哪取值需要和院方及his讨论。
+         */
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            return;
+        }
+        boolean emergencyOperation = false;
+
+        FirstPageRecordDoc firstPageRecordDoc = inputInfo.getFirstPageRecordDoc();
+        String behospitalWay = firstPageRecordDoc.getStructureMap().get("入院途径");
+        if (inputInfo.getBeHospitalizedDoc() != null) {
+            Map<String, String> beHospitalStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+            String admisTime = beHospitalStructureMap.get("入院日期");
+            //取手术记录第一次手术开始时间和入院时间比较,相差30分钟内不报缺陷
+            OperationRecordDoc operationRecordDoc = operationDocs.get(0).getOperationRecordDoc();
+            String startTime = "";
+            if (operationRecordDoc != null) {
+                startTime = operationRecordDoc.getStructureMap().get("手术开始时间");
+            }
+            if (StringUtil.isNotBlank(startTime) && StringUtil.isNotBlank(admisTime) &&
+                    !startTime.contains("年月日")) {
+                if (!CatalogueUtil.compareTime(
+                        StringUtil.parseDateTime(admisTime),
+                        StringUtil.parseDateTime(startTime),
+                        Long.valueOf(30))) {//入院到手术未超过30分钟,则可不用写术前小结
+                    emergencyOperation = true;
+                }
+
+                //判断是否是急诊手术;[判断入院途径:急诊,这种情况不用术前讨论]
+                if(StringUtil.isNotBlank(behospitalWay)&&"急诊".equals(behospitalWay)
+                        &&!CatalogueUtil.compareTime(
+                        StringUtil.parseDateTime(admisTime),
+                        StringUtil.parseDateTime(startTime),
+                        Long.valueOf(60)))
+                {
+                    emergencyOperation = true;
+
+                }
+            }
+        }
+        //北仑传入急诊标志,在首次病程录中
+        if (inputInfo.getFirstCourseRecordDoc() != null) {
+            Map<String, String> structureMap = inputInfo.getFirstCourseRecordDoc().getStructureMap();
+            String title = structureMap.get("标题");
+            String diagPlan = structureMap.get("诊疗计划");
+            if (StringUtil.isNotBlank(title) && title.contains("急诊") && StringUtil.isNotBlank(diagPlan) && diagPlan.contains("介入")) {
+                return;
+            }
+        }
+
+        int i = getOperationSum(operationDocs); // 获取手术记录次数
+        int j = 0;  // 获取术前讨论、术前小结次数
+        for (OperationDoc operationDoc : operationDocs) {
+            if (operationDoc.getPreoperativeDiscussionDoc() != null) {
+                j++;
+            }
+        }
+        /* 如果入院时间-手术开始时间小于30分钟,则术前讨论、术前小结次数+1*/
+        if (emergencyOperation) {
+            if (j == 0) {
+                j++;
+            }
+        }
+        //医嘱中包含“冠状动脉造影术”,且无术前讨论.则报规则
+        boolean OPSFlag = false;
+        boolean INFlag = false;
+        for (DoctorAdviceDoc doctorAdviceDoc : doctorAdviceDocs) {
+            Map<String, String> doctorAdviceStructuerMap = doctorAdviceDoc.getStructureMap();
+            String advicename = doctorAdviceStructuerMap.get("医嘱项目名称");
+            if (StringUtil.isNotBlank(advicename) && advicename.contains("冠状动脉造影术")) {
+                OPSFlag = true;
+            }
+            if (StringUtil.isNotBlank(advicename) && advicename.contains("介入")) {
+                INFlag = true;
+            }
+        }
+        //如果存在冠状动脉造影术,不存在介入治疗
+        if (OPSFlag && !INFlag && j == 0) {
+            status.set("-1");
+            info.set("手术记录不一致");
+        }
+
+        if (i > 0 && i > j) {
+            status.set("-1");
+            info.set("手术记录不一致");
+        }
+    }
+
+    /**
+     * 获取手术记录次数
+     *
+     * @param operationDocs
+     * @return
+     */
+    private int getOperationSum(List<OperationDoc> operationDocs) {
+        List<Map<String, Date>> operationDateList = new ArrayList<>();
+        Map<String, Date> operationDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> structureMap = operationRecordDoc.getStructureMap();
+            String operationStartDateStr = structureMap.get("手术开始时间");
+            if (StringUtil.isNotBlank(operationStartDateStr) && !operationStartDateStr.contains("年月日")) {
+                Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                operationStartDate = DateUtil.dateZeroClear(operationStartDate);
+                if (operationStartDate != null) {
+                    /* 放第一个手术记录的日期到operationDateList */
+                    if (operationDateList.size() == 0) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术开始时间", operationStartDate);
+                        operationDateList.add(operationDate);
+                        continue;
+                    }
+                    /* 如果其中一个手术记录的开始时间到结束时间之间还包含另一个手术,就不往operationDateList里加 */
+                    boolean findInnerOperation = false;
+                    for (Map<String, Date> date : operationDateList) {
+                        Date listStartDate = DateUtil.dateZeroClear(date.get("手术开始时间"));
+                        if (listStartDate.equals(operationStartDate)) {
+                            findInnerOperation = true;
+                            break;
+                        }
+                    }
+                    if (!findInnerOperation) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术开始时间", operationStartDate);
+                        operationDateList.add(operationDate);
+                    }
+                }
+            }
+        }
+        return operationDateList.size();
+    }
+
+}

+ 99 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR0134.java

@@ -0,0 +1,99 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.threelevelward;
+
+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.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.ward.AttendingDoctorWardDoc;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+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;
+
+/**
+ * @ClassName : THR0134
+ * @Description : 上级医师(主治医师)首次查房无初步诊断
+ * @Author : 胡敬
+ * @Date: 2020-03-23 14:16
+ */
+@Component
+public class THR0134 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getThreeLevelWardDocs().size() == 0 || inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        List<AttendingDoctorWardDoc> attendDocs = inputInfo.getThreeLevelWardDocs().get(0).getAttendingDoctorWardDocs();
+        if (attendDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
+        AttendingDoctorWardDoc firstAttendDoc = attendDocs.get(0);
+        //先取结构化数据判断
+        Map<String, String> firstAttendStructureMap = firstAttendDoc.getStructureMap();
+        String admisDateStr = inputInfo.getBeHospitalizedDoc().getStructureMap().get("入院日期");
+        String recordDateStr = firstAttendStructureMap.get("查房日期");
+        String content = firstAttendStructureMap.get("病情记录");
+        if (content.contains("诊断明确") || content.contains("目前诊断") || content.contains("目前考虑") || content.contains("当前诊断") ||
+                content.contains("考虑诊断") || content.contains("诊断考虑") || content.contains("诊断基本明确") || content.contains("初步诊断") ||
+                content.contains("诊断为") || regexFind(content, "诊断", "基本明确")|| content.contains("查房认为")) {
+            status.set("0");
+            return;
+        }
+        if (CatalogueUtil.isEmpty(admisDateStr) || CatalogueUtil.isEmpty(recordDateStr)) {
+            status.set("0");
+            return;
+        }
+        //如果首次查房超过48小时则不判断该条规则
+        if (CatalogueUtil.compareTime(StringUtil.parseDateTime(admisDateStr), StringUtil.parseDateTime(recordDateStr), 48 * 60L)) {
+            status.set("0");
+            return;
+        }
+        //如果首次查房在手术记录之后不提示规则
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs != null) {
+            for (OperationDoc operationDoc : operationDocs) {
+                if (operationDoc.getOperationRecordDoc() != null) {
+                    Map<String, String> structureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+                    String opeEndDate = structureMap.get("手术结束时间");
+                    if (StringUtil.isBlank(opeEndDate)) {
+                        opeEndDate = structureMap.get("记录时间");
+                    }
+                    String regex = ".*\\d.*";
+                    if (!opeEndDate.matches(regex)) {
+                        continue;
+                    }
+                    if (StringUtil.isNotBlank(opeEndDate) &&
+                            StringUtil.parseDateTime(opeEndDate).before(StringUtil.parseDateTime(recordDateStr))) {
+                        status.set("0");
+                        return;
+                    }
+                }
+            }
+        }
+        ThreeLevelWardLabel firstAttendLabel = firstAttendDoc.getThreeLevelWardLabel();
+        if (firstAttendLabel == null) {
+            return;
+        }
+
+        if (firstAttendLabel.getDiags().size() != 0 || StringUtil.isNotBlank(firstAttendLabel.getDiagText())) {
+            status.set("0");
+        }
+    }
+
+    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();
+    }
+}

+ 125 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR0139.java

@@ -0,0 +1,125 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.threelevelward;
+
+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.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.ward.DirectorDoctorWardDoc;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+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;
+
+/**
+ * @ClassName : THR0139
+ * @Description : 上级医师(副主任医师/主任医师)首次查房无补充诊断/初步诊断/修正诊断
+ * @Author : 胡敬
+ * @Date: 2020-03-23 14:16
+ */
+@Component
+public class THR0139 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getThreeLevelWardDocs().size() == 0 || inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        List<DirectorDoctorWardDoc> directorDocs = inputInfo.getThreeLevelWardDocs().get(0).getDirectorDoctorWardDocs();
+        if (directorDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
+        DirectorDoctorWardDoc firstDirectDoc = directorDocs.get(0);
+        //先取结构化数据判断
+        Map<String, String> firstDirectStructureMap = firstDirectDoc.getStructureMap();
+        String admisDateStr = inputInfo.getBeHospitalizedDoc().getStructureMap().get("入院日期");
+        String recordDateStr = firstDirectStructureMap.get("查房日期");
+        String content = firstDirectStructureMap.get("病情记录");
+        if (content.contains("诊断明确") || content.contains("目前诊断") || content.contains("目前考虑") || content.contains("当前诊断") ||
+                content.contains("考虑诊断") || content.contains("诊断考虑") || content.contains("诊断基本明确") || content.contains("初步诊断") ||
+                content.contains("诊断为") || regexFind(content, "诊断", "基本明确")) {
+            status.set("0");
+            return;
+        }
+
+        if (CatalogueUtil.isEmpty(admisDateStr) || CatalogueUtil.isEmpty(recordDateStr)) {
+            status.set("0");
+            return;
+        }
+        //如果首次查房超过72小时则不判断该条规则
+        if (CatalogueUtil.compareTime(StringUtil.parseDateTime(admisDateStr), StringUtil.parseDateTime(recordDateStr), 72 * 60L)) {
+            status.set("0");
+            return;
+        }
+        //如果首次查房在手术记录之后不提示规则
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs != null) {
+            for (OperationDoc operationDoc : operationDocs) {
+                if (operationDoc.getOperationRecordDoc() != null) {
+                    Map<String, String> structureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+                    String opeEndDate = structureMap.get("手术结束时间");
+                    if (StringUtil.isBlank(opeEndDate)) {
+                        opeEndDate = structureMap.get("记录时间");
+                    }
+                    String regex = ".*\\d.*";
+                    if (!opeEndDate.matches(regex)) {
+                        continue;
+                    }
+                    if (StringUtil.isNotBlank(opeEndDate) &&
+                            StringUtil.parseDateTime(opeEndDate).before(StringUtil.parseDateTime(recordDateStr))) {
+                        status.set("0");
+                        return;
+                    }
+                }
+            }
+        }
+        //===========三级医师相当于主治医生==========
+        ThreeLevelWardDoc threeLevelWardDoc = inputInfo.getThreeLevelWardDocs().get(0);
+        List<ThreeLevelWardDoc>  allDoctorWradDocs =  threeLevelWardDoc.getAllDoctorWradDocs();
+        if(allDoctorWradDocs.size()>0)
+        {
+
+            for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+                Map<String, String> structureMap = threeLevelWard.getStructureMap();
+                String makeTitle = structureMap.get("查房标题");
+                if(StringUtil.isNotBlank(makeTitle)&&makeTitle.contains("三级"))
+                {
+                    String recordDateStrs = structureMap.get("查房日期");
+                    if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisDateStr),
+                            StringUtil.parseDateTime(recordDateStrs), 72 * 60L)) {
+
+                        status.set("0");
+                    }
+                    return;
+                }
+            }
+
+
+        }
+
+        ThreeLevelWardLabel firstDirectLabel = firstDirectDoc.getThreeLevelWardLabel();
+        if (firstDirectLabel == null) {
+            return;
+        }
+
+        if (firstDirectLabel.getDiags().size() != 0 || StringUtil.isNotBlank(firstDirectLabel.getDiagText())) {
+            status.set("0");
+        }
+    }
+
+    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();
+    }
+}

+ 348 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR03076.java

@@ -0,0 +1,348 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.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.LeaveHospitalDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+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.DrugLabel;
+import com.lantone.qc.pub.model.label.LeaveHospitalLabel;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.time.DateUtils;
+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-26 10:51
+ * @desc 医嘱与病程记录抗生素剂量不一致
+ **/
+@Component
+public class THR03076 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();
+        //获取首次查房记录
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        //获取会诊信息
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
+        //获取手术记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        //获取出院小结
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.size() == 0) {
+            return;
+        }
+
+        Map<Date, String> extData = null;
+        if (outputInfo.getResult().get("THR02985") != null) {
+            extData = (Map<Date, String>) outputInfo.getResult().get("THR02985").get("extData");
+        }
+
+        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("医嘱单次剂量")))
+//                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+        docAdvStruct.removeIf(x -> StringUtil.isNotBlank(x.get("给药方式")) && !filterKey.contains(x.get("给药方式")));
+
+        //抗生素及开医嘱时间 <抗生素名,<抗生素用量,[抗生素使用时间...]>>
+        Map<String, Map<String, List<Double>>> antibioticInfo = Maps.newLinkedHashMap();
+        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
+        //记录同一抗生素同一天内是否开过多次,用于医嘱中需要处理的抗生素过滤(一天内同一抗生素开过多次的抗生素直接过滤)
+        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
+        String drugName = null, value = null, startDateStr = null;
+        Date startDate = null;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+//            startDate = StringUtil.parseDateTime(startDateStr);
+            if (extData != null && extData.containsKey(startDate) && extData.get(startDate).equals(drugName)) {
+                continue;   //THR02985  医嘱有抗生素使用病程无记录,规则中没报未记录的抗生素继续走这条规则,报未记录的抗生素过滤
+            }
+            if (antibioticDateTimes.get(drugName).get(startDate) > 0) {
+                continue;   //一天内同一抗生素开过多次的抗生素直接过滤
+            }
+            collectAntibioticInfo(antibioticInfo, drugName, value, startDateStr);
+        }
+
+        //抗生素及开医嘱时间 <抗生素名,<抗生素用量,[抗生素使用时间...]>>
+        Map<String, Map<String, List<Double>>> antibioticWardInfo = Maps.newLinkedHashMap();
+        String dateStr = null;
+        /*********************************************首程治疗计划********************************************************/
+        if (firstCourseRecordDoc != null) {
+            DrugLabel drugLabel = firstCourseRecordDoc.getDrugLabel();
+            dateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
+            if (drugLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = drugLabel.getDrugs();
+                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
+            }
+        }
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+                if (doc.getThreeLevelWardLabel().size() == 0) {
+                    continue;
+                }
+                dateStr = doc.getStructureMap().get("查房日期");
+                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+                List<Drug> drugs = label.getDrugs();
+                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
+            }
+        }
+        /**********************************************手术记录、术后首程************************************************/
+        if (operationDocs.size() > 0) {
+            //手术记录
+            List<OperationRecordDoc> operationRecordDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationRecordDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationRecordLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("手术时间")))
+                    .collect(Collectors.toList());
+            operationRecordDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getOperationRecordLabel().getDrugs(), x.getStructureMap().get("手术时间")));
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("病历日期")))
+                    .collect(Collectors.toList());
+            operationDiscussionDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getOperationDiscussionLabel().getDrugs(), x.getStructureMap().get("病历日期")));
+        }
+        /*********************************************会诊结果单********************************************************/
+        /*if (consultationDocs.size() > 0) {
+            List<ConsultationResultsDoc> consultationResultsDocs = consultationDocs
+                    .stream()
+                    .map(ConsultationDoc::getConsultationResultsDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getConsultationResultLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("会诊日期及时间")))
+                    .collect(Collectors.toList());
+            consultationResultsDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getConsultationResultLabel().getDrugs(), x.getStructureMap().get("会诊日期及时间")));
+        }*/
+        /*********************************************出院小结********************************************************/
+        if (leaveHospitalDoc != null) {
+            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
+            if (inputInfo.getMedicalRecordInfoDoc() != null) {
+                Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+                dateStr = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+                //如果存在出院小结,出院日期为空,存储系统当前时间
+                if (StringUtil.isBlank(dateStr)) {
+                    dateStr = DateUtil.formatDateTime(new Date());
+                }
+            }
+            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = leaveHospitalLabel.getDrugs();
+                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
+            }
+        }
+
+        /**
+         * 1.医嘱中开了抗生素,查房记录中没有该抗生素,则医嘱中该抗生素出现过的所有时间都会提示出来
+         * 2.医嘱中开了抗生素,查房记录中有该抗生素:
+         *      2.1 医嘱中该抗生素某使用量(如50),查房记录中没有该使用量(如只有100),则医嘱中该抗生素使用量出现过的所有时间都会提示出来
+         *      2.2 医嘱中该抗生素某使用量(如50),查房记录中也有该使用量,对比这两个时间,若医嘱时间两天内的查房记录没有该使用量,则该医嘱时间会提示出来
+         */
+        StringBuffer sb = new StringBuffer();
+        String drugKey = null;
+        for (Map.Entry<String, Map<String, List<Double>>> ai : antibioticInfo.entrySet()) {
+            drugKey = ai.getKey();
+            drugKey = removeBracket(drugKey).replaceAll("[^\u4e00-\u9fa5]", "");
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            if (antibioticWardInfo.containsKey(drugKey)) {
+                Map<String, List<Double>> adDateValue = ai.getValue();
+                Map<String, List<Double>> wardDateValue = antibioticWardInfo.get(drugKey);
+                for (Map.Entry<String, List<Double>> adMap : adDateValue.entrySet()) {
+                    String adDateStr = adMap.getKey();
+                    Date adDate = StringUtil.parseDateTime(adDateStr);
+                    List<Double> adUsage = adMap.getValue();
+                    StringBuffer innersb = new StringBuffer();
+                    for (Map.Entry<String, List<Double>> wdvMap : wardDateValue.entrySet()) {
+                        String wardDateStr = wdvMap.getKey();
+                        Date wardDate = StringUtil.parseDateTime(wardDateStr);
+                        List<Double> wardUsage = wdvMap.getValue();
+                        if ((adDate.before(wardDate) && !CatalogueUtil.compareTime(adDate, wardDate, 48 * 60L))
+                                || (wardDate.before(adDate) && !CatalogueUtil.compareTime(wardDate, adDate, 24 * 60L)) ||
+                                DateUtils.isSameDay(wardDate, adDate)) {
+                            wardUsage.removeAll(adUsage);//比如adUsage有1.0、2.0,wardUsage中有2.0、3.0,removeAll之后wardUsage只剩3.0
+                            adDateStr = DateUtil.formatDateTime(adDate);
+                            if (wardUsage.size() > 0 && !sb.toString().contains(ai.getKey() + "(" + adDateStr + ")")) {
+                                infoAppend(innersb, ai.getKey(), adDateStr);
+                            }
+                            if(wardUsage.size() == 0){
+                                innersb.delete(0,innersb.length());
+                                break;
+                            }
+                        }
+                    }
+                    sb.append(innersb);
+                }
+            }
+        }
+
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set("医嘱:" + sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 记录同一抗生素同一天内是否开过多次,用于医嘱中需要处理的抗生素过滤(一天内同一抗生素开过多次的抗生素直接过滤)
+     *
+     * @param docAdvStruct
+     * @param antibioticDateTimes
+     */
+    private void getAntibioticTimes(List<Map<String, String>> docAdvStruct, Map<String, Map<Date, Integer>> antibioticDateTimes) {
+        String drugName;
+        String startDateStr;
+        Date startDate;
+        Map<Date, Integer> antibioticDateTime;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+//            startDate = StringUtil.parseDateTime(startDateStr);
+            if (antibioticDateTimes.containsKey(drugName)) {
+                Map<Date, Integer> map = antibioticDateTimes.get(drugName);
+                if (map.containsKey(startDate)) {
+                    map.put(startDate, map.get(startDate) + 1);
+                } else {
+                    map.put(startDate, 0);
+                }
+            } else {
+                antibioticDateTime = Maps.newHashMap();
+                antibioticDateTime.put(startDate, 0);
+                antibioticDateTimes.put(drugName, antibioticDateTime);
+            }
+        }
+    }
+
+    private void getCourseDrugInfo(Map<String, Map<String, List<Double>>> antibioticWardInfo, List<Drug> drugs, String dateStr) {
+        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(antibioticWardInfo, wardDrug, consumption, dateStr);
+            }
+        }
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(date).append(")").append("_");
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @param antibioticInfo 抗生素使用量及所有时间
+     * @param drugName       抗生素名称
+     * @param value          抗生素用量
+     * @param startDateStr   抗生素使用时间(医嘱开始时间或查房时间)
+     */
+    private void collectAntibioticInfo(Map<String, Map<String, List<Double>>> antibioticInfo, String drugName, String value, String startDateStr) {
+        Map<String, List<Double>> antibioticValueList = null;
+        double v = -1;
+        try {
+            v = Double.parseDouble(getNumber(value));
+        } catch (Exception e) {
+            System.out.println("THR03076:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (v > 100) {
+            v = v / 1000;
+        }
+        if (!antibioticInfo.containsKey(drugName)) {
+            //存该抗生素使用第1个值
+            antibioticValueList = Maps.newLinkedHashMap();
+            antibioticValueList.put(startDateStr, Lists.newArrayList(v));
+            antibioticInfo.put(drugName, antibioticValueList);
+        } else {
+            antibioticValueList = antibioticInfo.get(drugName);
+            //存该抗生素使用时间时第n个量
+            if (antibioticValueList.containsKey(startDateStr)) {
+                antibioticValueList.get(startDateStr).add(v);
+            } else {
+                //存该抗生素使用时间时第1个量
+                antibioticValueList.put(startDateStr, Lists.newArrayList(v));
+            }
+        }
+    }
+
+    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;
+    }
+
+    private static final List<String> filterKey = Lists.newArrayList("ACF", "ID", "IG", "IM", "IP", "IV",
+            "关节腔注射", "宫颈注射", "皮下注射", "皮下注射(儿童)", "皮下注射(免费)", "皮下注射(成人)", "皮内", "皮内注射",
+            "结膜下注射", "肌注", "肌肉注射(儿童)", "肌肉注射(公卫专用)", "肌肉注射(成人)", "胸腔注射", "腹腔内注射", "腹腔注射",
+            "静滴(儿童)", "静滴(成人)", "静脉注射", "静脉注射(儿童)", "静脉注射(免费)", "静脉注射(成人)", "静脉注射(泵)",
+            "静脉滴注", "静脉滴注(泵)", "鞘内注射", "微泵");
+
+}

+ 101 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/beilun/threelevelward/THR03090.java

@@ -0,0 +1,101 @@
+package com.lantone.qc.kernel.catalogue.hospital.beilun.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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR03090
+ * @Description : 手术患者无术前主刀医师查房记录
+ * @Author : wsy
+ * @Date: 2021-01-11 10:39
+ */
+@Component
+@Slf4j
+public class THR03090 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        long cou = inputInfo.getOperationDocs().stream().map(OperationDoc::getOperationRecordDoc).filter(Objects::nonNull).count();
+        if (cou == 0) {
+            return;
+        }
+        if (threeLevelWardDocs.size() == 0 && cou > 0) {
+            status.set("-1");
+            return;
+        }
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            return;
+        }
+
+        //北仑传入急诊标志,在首次病程录中
+        if (inputInfo.getFirstCourseRecordDoc() != null) {
+            Map<String, String> structureMap = inputInfo.getFirstCourseRecordDoc().getStructureMap();
+            String title = structureMap.get("标题");
+            if (StringUtil.isNotBlank(title) && title.contains("急诊")) {
+                return;
+            }
+        }
+
+        String operationStartDate = "";
+        try {
+            if (operationDocs.get(operationDocs.size() - 1).getOperationRecordDoc() != null) {
+                Map<String, String> operationDocStructureMap = operationDocs.get(operationDocs.size() - 1).getOperationRecordDoc().getStructureMap();
+                operationStartDate = operationDocStructureMap.get("手术开始时间");
+                if (operationStartDate.contains("年月日")) {
+                    operationStartDate = DateUtil.nowString();
+                }
+            }
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+                Map<String, String> structureMap = threeLevelWard.getStructureMap();
+                String makeTitle = structureMap.get("查房标题");
+                String writTitle = structureMap.get("文书标题");
+                String makeDate = structureMap.get("查房日期");
+                if (StringUtil.isNotBlank(makeDate) && StringUtil.parseDateTime(makeDate).before(StringUtil.parseDateTime(operationStartDate))) {//时间为空会报错
+                    if (((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("主刀")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("主刀"))
+                            || ((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("术前")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("术前"))))) {
+                        return;
+                    }
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            log.error(this.getClass().getName()+"error:"+e.toString());
+            return;
+        }
+
+        //判断医嘱里有无手术
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        if (doctorAdviceDocs.size() == 0) {
+            return;
+        }
+        for (DoctorAdviceDoc dad : doctorAdviceDocs) {
+            String name = dad.getStructureMap().get("医嘱项目名称");
+            if (name.contains("非手术") || name.contains("手术室") || (name.contains("手术") && name.contains("取消")) || (name.contains("暂停") && name.contains("手术")) || name.contains("静脉穿刺置管术") || name.startsWith("停") || name.contains("前一次")
+                    || name.contains("特殊病人手术使用一次性卫生材料") || name.contains("人免疫缺陷病毒抗体检测免费")) {
+                continue;
+            }
+            if (name.contains("手术")) {
+                status.set("-1");
+                return;
+            }
+        }
+
+        if (cou > 0) {
+            status.set("-1");
+            return;
+        }
+    }
+}

+ 235 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0011.java

@@ -0,0 +1,235 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.alibaba.fastjson.JSONArray;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.ChiefPresentSimilarityServiceClient;
+import com.lantone.qc.kernel.structure.ai.ModelAI;
+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.entity.Diag;
+import com.lantone.qc.pub.model.entity.GeneralDesc;
+import com.lantone.qc.pub.model.label.DiagLabel;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.model.label.PresentLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : BEH0011
+ * @Description : 初步诊断不完整
+ * 原逻辑 --初步诊断没有既往史里的慢性疾病
+ * 现逻辑 --检查现病史中一般情况之后的疾病名称,既往史中的疾病名称是否都在初步诊断里
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class BEH0011 extends QCCatalogue {
+    //    @Autowired
+    //    private SpecialStorageUtil specialStorageUtil;
+    @Autowired
+    ChiefPresentSimilarityServiceClient chiefPresentSimilarityServiceClient;
+
+    private List<String> containList = Arrays.asList("脑萎缩", "慢性", "纤颤", "高血压", "糖尿", "冠状", "冠心病", "支架", "起搏器", "房颤", "风湿");
+    private List<String> filterList = Arrays.asList("心脏病", "低血糖", "急性", ";");
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        String age = structureMap.get("年龄");
+        if (StringUtil.isNotBlank(age)) {
+            if (age.contains("月") || age.contains("天")) {
+                status.set("0");
+                return;
+            }
+            age = age.replaceAll("岁", "");
+            try {
+                if (Integer.parseInt(age) < 10) {
+                    status.set("0");
+                    return;
+                }
+            } catch (Exception e) {
+                System.out.println("BEH0011--解析age出错:" + "->" + age);
+            }
+        }
+        PresentLabel presentLabel = inputInfo.getBeHospitalizedDoc().getPresentLabel();
+        DiagLabel initialDiagLabel = inputInfo.getBeHospitalizedDoc().getInitialDiagLabel();
+        /*补充诊断*/
+        DiagLabel suppleDiagLabel = inputInfo.getBeHospitalizedDoc().getSuppleDiagLabel();
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        if (initialDiagLabel == null) {
+            status.set("0");
+            return;
+        }
+        Set<String> presentPastDiags = new HashSet<>();
+        if (presentLabel != null) {
+            List<GeneralDesc> generals = presentLabel.getGenerals();
+            if (generals.size() > 0) {
+                String presentText = presentLabel.getText();
+                List<Diag> presentDiags = presentLabel.getDiags();
+                /* 取现病史中一般情况之后的疾病名称 */
+                if (StringUtil.isNotBlank(presentText) && presentDiags.size() > 0) {
+                    String lastGeneral = generals.get(generals.size() - 1).getName();
+                    int lastGeneralIndex = presentText.indexOf(lastGeneral);
+                    for (Diag presentDiag : presentDiags) {
+                        if (presentDiag.getNegative() != null || presentDiag.getHospitalDiagName().contains("否认")) {
+                            continue;
+                        }
+                        /* 现病史中一般情况之后的疾病名称 */
+                        if (presentText.indexOf(presentDiag.getHospitalDiagName()) > lastGeneralIndex) {
+                            if (isContains(presentDiag.getHospitalDiagName()) && !isFilter(presentDiag.getHospitalDiagName())) {
+                                presentPastDiags.add(presentDiag.getHospitalDiagName());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        /* 取既往史中疾病名称 */
+        if (pastLabel != null && StringUtils.isNotEmpty(pastLabel.getText())) {
+            List<Diag> pastDiags = pastLabel.getDiags();
+            addDiagHospitalName(presentPastDiags, pastDiags);
+        } else {            //结构化数据
+            List<Diag> pastDiags = new ArrayList<>();
+
+//            Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+            String a1 = structureMap.get("高血压");
+            if (StringUtils.isNotEmpty(a1) && !"否认".equals(a1)) {
+                Diag diag = new Diag();
+                diag.setName("高血压");
+                diag.setHospitalDiagName("高血压");
+                if (!isHave(presentPastDiags, "高血压")) {
+                    pastDiags.add(diag);
+                }
+            }
+            a1 = structureMap.get("糖尿病");
+            if (StringUtils.isNotEmpty(a1) && !"否认".equals(a1)) {
+                Diag diag = new Diag();
+                diag.setName("糖尿病");
+                diag.setHospitalDiagName("糖尿病");
+                if (!isHave(presentPastDiags, "糖尿病")) {
+                    pastDiags.add(diag);
+                }
+            }
+            addDiagHospitalName(presentPastDiags, pastDiags);
+        }
+        /* 取初步诊断中疾病名称 */
+        List<String> initDiags = new ArrayList<>();
+        List<Diag> initialDiagDiags = initialDiagLabel.getDiags();
+        addInitDiagHospitalName(initDiags, initialDiagDiags);
+        List<Diag> suppleDiagDiags = suppleDiagLabel.getDiags();
+        addInitDiagHospitalName(initDiags, suppleDiagDiags);
+        initDiags = initDiags.stream().distinct().collect(Collectors.toList());
+
+        String infoStr = "";
+        int matchSum = 0;
+        ModelAI modelAI = new ModelAI();
+        for (String presentPastDiag : presentPastDiags) {
+            JSONArray jsonArray = modelAI.loadChiefPresentSimilarAI(presentPastDiag, initDiags, false
+                    , "diagnose", chiefPresentSimilarityServiceClient);
+            if (jsonArray.size() == 2) {
+                /* 相似度最高症状 */
+                String symptom = jsonArray.getString(0);
+                /* 相似度分数 */
+                double likeRate = jsonArray.getDoubleValue(1);
+                if (likeRate > 0.80) {
+                    matchSum++;
+                } else {
+                    infoStr = CatalogueUtil.concatInfo(infoStr, presentPastDiag);
+                }
+            } else {
+                infoStr = CatalogueUtil.concatInfo(infoStr, presentPastDiag);
+            }
+        }
+        info.set(infoStr);
+        if (matchSum == presentPastDiags.size()) {
+            status.set("0");
+        }
+        /*
+        if (initDiags.containsAll(presentPastDiags)) {
+            status.set("0");
+        }
+         */
+    }
+
+    private void addDiagHospitalName(Set<String> presentPastDiag, List<Diag> pastDiags) {
+        for (Diag pastDiag : pastDiags) {
+            if (pastDiag.getNegative() != null) {
+                continue;
+            }
+            if (isContains(pastDiag.getHospitalDiagName()) && !isFilter(pastDiag.getHospitalDiagName())) {
+                presentPastDiag.add(pastDiag.getHospitalDiagName());
+            }
+        }
+    }
+
+    private void addInitDiagHospitalName(List<String> presentPastDiag, List<Diag> pastDiags) {
+        for (Diag pastDiag : pastDiags) {
+            if (pastDiag.getNegative() != null) {
+                continue;
+            }
+            presentPastDiag.add(pastDiag.getHospitalDiagName());
+        }
+    }
+
+    private boolean isHave(Set<String> presentDiag, String diagName) {
+        for (String haveDiag : presentDiag) {
+            if (haveDiag.contains(diagName) || diagName.contains(haveDiag)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isContains(String diagName) {
+        for (String c : containList) {
+            if (diagName.contains(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isFilter(String diagName) {
+        for (String c : filterList) {
+            if (diagName.contains(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    /* 原逻辑
+    Map<String, Map<String, Object>> diagMap = specialStorageUtil.getJsonStringValue(KernelConstants.CONCEPT_DIAG_PROPERTY_MAP);
+    String initDiagText = CatalogueUtil.removeSpecialChar(initialDiagLabel.getText());
+        if (pastLabel.getDiags() != null) {
+            List<Diag> diags = pastLabel.getDiags().stream().filter(diag -> diag.getNegative() == null).collect(Collectors.toList());
+            List<String> initDiags = Arrays.asList(initDiagText.split(","));
+            if (diags.size() > 0) {
+                for (Diag diag : diags) {
+                    Map<String, Object> map = (Map<String, Object>) diagMap.get(diag.getName());
+                    if (map != null && map.size() > 0) {
+                        String chronic = (String) map.get("chronic");//1是慢病
+                        if ("1".equals(chronic) && !initDiags.contains(diag.getName())) {
+                            if (StringUtils.isEmpty(info.get())) {
+                                info.set(diag.getName());
+                            } else {
+                                info.set(info.get() + "," + diag.getName());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (StringUtils.isEmpty(info.get())) {
+            status.set("0");
+        }
+     */
+}

+ 43 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0023.java

@@ -0,0 +1,43 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.*;
+import com.lantone.qc.pub.model.label.PresentLabel;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+
+/**
+ * @ClassName : BEH0023
+ * @Description :  现病史未描述入院前有无相关治疗检查
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class BEH0023 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            return;
+        }
+        PresentLabel presentLabel = inputInfo.getBeHospitalizedDoc().getPresentLabel();
+        if (presentLabel == null) {
+            return;
+        }
+        List<Lis> lises = presentLabel.getLises();
+        List<Pacs> pacses = presentLabel.getPacses();
+        List<Treat> treats = presentLabel.getTreats();
+        List<Operation> operations = presentLabel.getOperations();
+        List<Medicine> medicines = presentLabel.getMedicines();
+        if (lises == null && pacses.size() == 0 && treats.size() == 0 && operations.size() == 0 && medicines.size() == 0) {
+            status.set("-1");
+        }
+        //添加硬规则
+        if (inputInfo.getBeHospitalizedDoc().getPresentLabel().getText().contains("未予以重视")) {
+            status.set("0");
+        }
+    }
+}

+ 56 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0024.java

@@ -0,0 +1,56 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Wound;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 外伤史未填写
+ * @author: rengb
+ * @time: 2020/3/10 13:53
+ */
+@Component
+public class BEH0024 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> behStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructureMap.get("手术外伤史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        List<Wound> wounds = pastLabel.getWounds();
+        if (ListUtil.isNotEmpty(wounds)) {
+            if (wounds.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+        //硬规则匹配
+        String pastLabelText = pastLabel.getText();
+        if (pastLabelText.contains("外伤") || pastLabelText.contains("详见原病历")
+                || pastLabelText.contains("见旧病历") || pastLabelText.contains("见既往病历") || pastLabelText.contains("骨折")) {
+            status.set("0");
+        }
+    }
+}

+ 70 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0025.java

@@ -0,0 +1,70 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Diag;
+import com.lantone.qc.pub.model.entity.Operation;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 手术史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0025 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> behStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructureMap.get("手术外伤史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        List<Operation> operations = pastLabel.getOperations();
+        if (ListUtil.isNotEmpty(operations)) {
+            if (operations.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+        /* 疾病名称:**术后**,算是有手术史 */
+        List<Diag> diags = pastLabel.getDiags();
+        for (Diag diag : diags) {
+            String hospitalDiagName = diag.getHospitalDiagName();
+            if (StringUtil.isBlank(hospitalDiagName)) {
+                continue;
+            }
+            if (hospitalDiagName.contains("术后")) {
+                status.set("0");
+                return;
+            }
+        }
+        //规则硬匹配
+        String pastLabelText = pastLabel.getText();
+        if (pastLabelText.contains("手术") || pastLabelText.contains("详见原病历")
+                || pastLabelText.contains("见旧病历") || pastLabelText.contains("见既往病历")|| pastLabelText.contains("体外碎石")) {
+            status.set("0");
+        }
+    }
+
+}

+ 60 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0026.java

@@ -0,0 +1,60 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Allergy;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 食物过敏史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0026 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("过敏史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String pastText = pastLabel.getText();
+        if (pastText.contains("食物过敏史") || pastText.contains("详见原病历")|| pastText.contains("见旧病历") || pastText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        List<Allergy> allergies = pastLabel.getAllergies();
+        if (ListUtil.isNotEmpty(allergies)) {
+            long count = allergies.stream().filter(
+                    i -> i != null
+                            && StringUtil.isNotBlank(i.getName())
+                    //                            && i.getAllergyFood() != null
+                    //                            && StringUtil.isNotBlank(i.getAllergyFood().getName())
+            ).count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+    }
+
+}

+ 54 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0028.java

@@ -0,0 +1,54 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.BloodTransfusion;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 输血史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0028 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> behStructure = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructure.get("输血史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String pastText = pastLabel.getText();
+        if (pastText.contains("输血") || pastText.contains("详见原病历")|| pastText.contains("见旧病历") || pastText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        List<BloodTransfusion> bloodTransfusions = pastLabel.getBloodTransfusions();
+        if (ListUtil.isNotEmpty(bloodTransfusions)) {
+            if (bloodTransfusions.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+            }
+        }
+    }
+
+}

+ 72 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0029.java

@@ -0,0 +1,72 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.BeHospitalizedDoc;
+import com.lantone.qc.pub.model.entity.Vaccinate;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.model.label.PersonalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 预防接种史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0029 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        //台州结构化
+        Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("预防接种史"))) {
+            status.set("0");
+            return;
+        }
+        //既往史
+        PastLabel pastLabel = beHospitalizedDoc.getPastLabel();
+        //个人史
+        PersonalLabel personalLabel = beHospitalizedDoc.getPersonalLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if ((pastLabel == null || StringUtil.isBlank(pastLabel.getText())) &&
+                (personalLabel == null || StringUtil.isBlank(personalLabel.getText()))) {
+            status.set("0");
+            return;
+        }
+        if (pastLabel != null) {
+            String pastText = pastLabel.getText();
+            if (StringUtil.isNotBlank(pastText) && (pastText.contains("详见原病历") || pastText.contains("预防接种")
+                    || pastText.contains("见旧病历") || pastText.contains("见既往病历"))) {
+                status.set("0");
+                return;
+            }
+            List<Vaccinate> vaccinates = pastLabel.getVaccinates();
+            if (ListUtil.isNotEmpty(vaccinates)) {
+                if (vaccinates.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                    status.set("0");
+                    return;
+                }
+            }
+        }
+        if (personalLabel != null) {
+            String personText = personalLabel.getText();
+            if (StringUtil.isNotBlank(personText) && (personText.contains("详见原病历") || personText.contains("预防接种"))) {
+                status.set("0");
+                return;
+            }
+        }
+    }
+}

+ 56 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0030.java

@@ -0,0 +1,56 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @Description: 传染病史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0030 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("结核病")) || StringUtils.isNotEmpty(structureMap.get("病毒性肝炎"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        if (ListUtil.isNotEmpty(
+                CatalogueUtil.filterDiagsByNature(
+                        pastLabel.getDiags(),
+                        "infectious",
+                        "1"
+                )
+        )) {
+            status.set("0");
+        }
+        String pastText = pastLabel.getText();
+        if (pastText.contains("肝炎") || pastText.contains("结核") || pastText.contains("详见原病历")
+                || pastText.contains("见旧病历") || pastText.contains("见既往病历") || pastText.contains("乙肝")) {
+            status.set("0");
+        }
+    }
+
+}

+ 69 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0031.java

@@ -0,0 +1,69 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.google.common.collect.Lists;
+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.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 慢病史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0031 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("高血压"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        if (pastLabel != null && StringUtil.isNotBlank(pastLabel.getText())) {
+            if (ListUtil.isNotEmpty(
+                    CatalogueUtil.filterDiagsByNature(
+                            pastLabel.getDiags(),
+                            "chronic",
+                            "1"
+                    )
+            )) {
+                status.set("0");
+            }
+            //硬规则匹配
+            String text = pastLabel.getText();
+            if (StringUtils.isNotEmpty(text)) {
+                List<String> words = Lists.newArrayList("高血压", "糖尿病", "阿尔茨海默病", "帕金森", "冠心病心律失常型"
+                        , "冠状动脉性心脏病", "冠状动脉粥样硬化性心脏病", "慢性肾炎综合征", "肾病", "慢性肾衰竭", "肾功能异常", "哮喘"
+                        , "肺结核", "腹膜透析", "慢性阻塞性肺病", "精神分裂症", "分裂情感性精神病", "双相情感障碍,目前为缓解状态", "前列腺增生"
+                        , "高脂血症", "高低密度脂蛋白胆固醇血症", "高胆固醇血症", "高甘油三酯血症", "骨质疏松", "慢性乙型病毒性肝炎"
+                        , "慢性庚型肝炎", "慢性肝炎", "慢性丁型肝炎", "慢性病毒性肝炎", "慢性丙型病毒性肝炎", "酒精性肝病", "脂肪肝"
+                        , "肝硬化", "肝恶性肿瘤", "肝恶性细胞瘤", "肝病", "类风湿性关节炎", "心脑血管", "内分泌","见旧病历","见既往病历");
+                for (String word : words) {
+                    if (text.contains(word)) {
+                        status.set("0");
+                        return;
+                    }
+                }
+            }
+        } else {
+            status.set("0");
+            return;
+        }
+    }
+
+}

+ 63 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0042.java

@@ -0,0 +1,63 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Address;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 出生地未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0042 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if(inputInfo.getBeHospitalizedDoc() == null){
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("出生地"))) {
+            status.set("0");
+            return;
+        }
+
+        String text = inputInfo.getBeHospitalizedDoc().getPersonalLabel().getText();
+        if (StringUtil.isBlank(text)) {
+            return;
+        }
+        List<Address> addresses = inputInfo.getBeHospitalizedDoc().getPersonalLabel().getAddresses();
+        if (ListUtil.isNotEmpty(addresses)) {
+            long count = addresses.stream().filter(i -> {
+                boolean flag = false;
+                if (i != null && StringUtil.isNotBlank(i.getName())) {
+                    if (i.getName().indexOf("出生") > -1) {
+                        flag = true;
+                    } else {
+                        Pattern pattern = Pattern.compile("[\\s\\S]*(出生|生长)[\\s\\S]{0,5}" + i.getName() + "[\\s\\S]*");
+                        flag = pattern.matcher(text).matches();
+                    }
+                }
+                return flag;
+            }).count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+        if (text.contains("出生")|| text.contains("见旧病历") || text.contains("见既往病历")) {
+            status.set("0");
+        }
+    }
+
+}

+ 70 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0043.java

@@ -0,0 +1,70 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Address;
+import com.lantone.qc.pub.model.label.PersonalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 居住地未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0043 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("联系地址")) || StringUtils.isNotEmpty(structureMap.get("现住址"))
+                || StringUtils.isNotEmpty(structureMap.get("家庭住址"))) {
+            status.set("0");
+            return;
+        }
+
+        PersonalLabel personalLabel = inputInfo.getBeHospitalizedDoc().getPersonalLabel();
+        if (personalLabel == null) {
+            status.set("0");
+            return;
+        }
+        String text = personalLabel.getText();
+        if (StringUtil.isBlank(text) || text.contains("居住") || text.contains("生长") || text.contains("生活") || text.contains("本地")
+                || text.contains("详见原病历")|| text.contains("见旧病历") || text.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        List<Address> addresses = personalLabel.getAddresses();
+        if (ListUtil.isNotEmpty(addresses)) {
+            long count = addresses.stream().filter(i -> {
+                boolean flag = false;
+                if (i != null && StringUtil.isNotBlank(i.getName())) {
+                    if (i.getName().indexOf("居住") > -1) {
+                        flag = true;
+                    } else {
+                        Pattern pattern = Pattern.compile("[\\s\\S]*(居住|生长)[\\s\\S]{0,5}" + i.getName() + "[\\s\\S]*");
+                        flag = pattern.matcher(text).matches();
+                    }
+                }
+                return flag;
+            }).count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+    }
+
+}

+ 53 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0047.java

@@ -0,0 +1,53 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Drinking;
+import com.lantone.qc.pub.model.label.PersonalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @Description: 饮酒史未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0047 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("饮酒"))) {
+            status.set("0");
+            return;
+        }
+        PersonalLabel personalLabel = inputInfo.getBeHospitalizedDoc().getPersonalLabel();
+        if (personalLabel == null || StringUtil.isBlank(personalLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String pastText = personalLabel.getText();
+        if (pastText.contains("饮酒") || pastText.contains("详见原病历")|| pastText.contains("见旧病历") || pastText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+
+        Drinking drinking = personalLabel.getDrinking();
+        if (drinking != null && StringUtil.isNotBlank(drinking.getName())) {
+            status.set("0");
+        }
+        //硬匹配规则
+        if (inputInfo.getBeHospitalizedDoc().getPersonalLabel().getText().contains("酒")) {
+            status.set("0");
+        }
+    }
+}

+ 65 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0053.java

@@ -0,0 +1,65 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.pub.Content;
+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.entity.Marryiage;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @Description: 结婚年龄未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0053 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        if (beHospitalizedDoc == null) {
+            status.set("0");
+            return;
+        }
+        //先取一次结构化数据
+        Map<String, String> beHospitalizedStructureMap = beHospitalizedDoc.getStructureMap();
+        String marryiAgeStr = beHospitalizedStructureMap.get("结婚年龄");
+        if (StringUtil.isNotBlank(marryiAgeStr)) {
+            status.set("0");
+            return;
+        }
+        //硬规则 匹配未婚
+        String marry = beHospitalizedDoc.getStructureMap().get(Content.marry);
+        if (StringUtil.isBlank(marry)) {
+            marry = beHospitalizedDoc.getStructureMap().get("婚姻状况");
+        }
+        MaritalLabel maritalLabel = beHospitalizedDoc.getMaritalLabel();
+        if (maritalLabel == null || StringUtil.isBlank(maritalLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String maritalText = maritalLabel.getText();
+        if ("未婚".equals(marry) || maritalText.contains("未婚") || maritalText.contains("详见原病历")
+                || maritalText.contains("离婚") || maritalText.contains("离异") || maritalText.contains("结婚")
+                || maritalText.contains("丧偶")|| maritalText.contains("见旧病历") || maritalText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        if (beHospitalizedDoc.getMaritalLabel() == null
+                || StringUtil.isBlank(beHospitalizedDoc.getMaritalLabel().getText())) {
+            status.set("0");
+        }
+
+        Marryiage marryiage = beHospitalizedDoc.getMaritalLabel().getMarryiage();
+        if (marryiage != null && StringUtil.isNotBlank(marryiage.getName())) {
+            status.set("0");
+        }
+    }
+
+}

+ 109 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0058.java

@@ -0,0 +1,109 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.google.common.collect.Lists;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.pub.Content;
+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.entity.Family;
+import com.lantone.qc.pub.model.entity.Fertility;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 子女数量情况未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0058 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        //硬规则 匹配未育
+        String marry = beHospitalizedDoc.getStructureMap().get(Content.marry);
+        MaritalLabel maritalLabel = beHospitalizedDoc.getMaritalLabel();
+        if (maritalLabel == null) {
+            status.set("0");
+            return;
+        }
+        Fertility fertility = maritalLabel.getFertility();
+        if (fertility != null) {
+            status.set("0");
+            return;
+        }
+        String maritalText = maritalLabel.getText();
+        if (StringUtil.isBlank(maritalText)) {
+            status.set("0");
+            return;
+        } else {
+            String regex = ".*\\d?子\\d?女.*";
+            boolean flag = maritalText.matches(regex);
+            regex = ".*\\d+-\\d+-\\d+-[1-9].*";
+            boolean matches = maritalText.matches(regex);
+            regex = ".*\\d+,\\d+,\\d+,[1-9].*|.*\\d个.*";
+            boolean isComma = maritalText.matches(regex);
+            if (flag || matches || isComma) {
+                status.set("0");
+                return;
+            }
+        }
+        //硬规则 匹配未育
+        if ("未婚".equals(marry)) {
+            status.set("0");
+            return;
+        }
+        if (StringUtil.isNotBlank(maritalText)) {
+            List<String> words = Lists.newArrayList("未婚", "未育", "未婚育", "未生育", "未生", "0子0女"
+                    , "0-0-0-0", "详见原病历", "0-0-0-0","见旧病历","见既往病历");
+            for (String word : words) {
+                if (maritalText.contains(word)) {
+                    status.set("0");
+                    return;
+                }
+            }
+        }
+        Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+        String familyMembersHealth = structureMap.get("家庭成员健康情况");
+        List<Family> familyList = Lists.newArrayList();
+        List<Family> familiesFl = inputInfo.getBeHospitalizedDoc().getFamilyLabel().getFamilies();
+        List<Family> familiesMl = inputInfo.getBeHospitalizedDoc().getMaritalLabel().getFamily();
+        if (ListUtil.isNotEmpty(familiesFl)) {
+            familyList.addAll(familiesFl);
+        }
+        if (ListUtil.isNotEmpty(familiesMl)) {
+            familyList.addAll(familiesMl);
+        }
+        Pattern p = Pattern.compile(".*[儿|子|女].*");
+        if (StringUtil.isNotBlank(familyMembersHealth)) {
+            if (p.matcher(familyMembersHealth).find()) {
+                status.set("0");
+                return;
+            }
+        }
+        long count = familyList
+                .stream()
+                .filter(
+                        i -> i != null
+                                && StringUtil.isNotBlank(i.getName())
+                                && p.matcher(i.getName()).find()
+                )
+                .count();
+        if (count > 0) {
+            status.set("0");
+        }
+    }
+
+}

+ 260 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH02980.java

@@ -0,0 +1,260 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.alibaba.fastjson.JSONArray;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.ChiefPresentSimilarityServiceClient;
+import com.lantone.qc.kernel.structure.ai.ModelAI;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.entity.Allergy;
+import com.lantone.qc.pub.model.entity.Diag;
+import com.lantone.qc.pub.model.entity.GeneralDesc;
+import com.lantone.qc.pub.model.entity.Negative;
+import com.lantone.qc.pub.model.label.DiagLabel;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.model.label.PresentLabel;
+import com.lantone.qc.pub.util.StringUtil;
+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.List;
+import java.util.Map;
+
+
+/**
+ * @ClassName : BEH02980
+ * @Description : 病历前后描述不一致
+ * @Author : Mark
+ * @Date: 2020-06-23 11:02
+ */
+@Component
+public class BEH02980 extends QCCatalogue {
+    @Autowired
+    ChiefPresentSimilarityServiceClient chiefPresentSimilarityServiceClient;
+
+    private List<String> containList = Arrays.asList("脑萎缩", "慢性", "纤颤", "高血压", "糖尿", "冠状", "冠心病", "支架", "起搏器", "房颤", "风湿");
+    private List<String> filterList = Arrays.asList("心脏病", "低血糖", "急性", ";");
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            return;
+        }
+        PresentLabel presentLabel = inputInfo.getBeHospitalizedDoc().getPresentLabel();
+        DiagLabel initialDiagLabel = inputInfo.getBeHospitalizedDoc().getInitialDiagLabel();
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+
+        List<String> pos_diags = new ArrayList<>();
+        List<String> neg_diags = new ArrayList<>();
+        //现病史需要取一般情况之后疾病
+        if (presentLabel != null) {
+            List<GeneralDesc> generals = presentLabel.getGenerals();
+            if (generals.size() > 0) {
+                String presentText = presentLabel.getText();
+                List<Diag> presentDiags = presentLabel.getDiags();
+                /* 取现病史中一般情况之后的疾病名称 */
+                if (StringUtil.isNotBlank(presentText) && presentDiags.size() > 0) {
+                    String lastGeneral = generals.get(generals.size() - 1).getName();
+                    int lastGeneralIndex = presentText.indexOf(lastGeneral);
+                    for (Diag presentDiag : presentDiags) {
+                        if (presentDiag.getNegative() != null || presentDiag.getHospitalDiagName().contains("否认")) {
+                            continue;
+                        }
+                        /* 现病史中一般情况之后的疾病名称 */
+                        if (presentText.indexOf(presentDiag.getHospitalDiagName()) > lastGeneralIndex) {
+                            if (isContains(presentDiag.getHospitalDiagName()) && !isFilter(presentDiag.getHospitalDiagName())) {
+                                String dgname = presentDiag.getHospitalDiagName();
+                                if (presentDiag.getNegative() == null) {
+                                    if (!pos_diags.contains(dgname)) {
+                                        pos_diags.add(dgname);
+                                    }
+                                } else {
+                                    if (!neg_diags.contains(dgname)) {
+                                        neg_diags.add(dgname);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        if (pastLabel != null && StringUtils.isNotEmpty(pastLabel.getText())) {
+            extract_diags(pastLabel.getDiags(), pos_diags, neg_diags);
+            //否认其他的情况
+            String pasttext = pastLabel.getText();
+            //冠状动脉支架植入术特殊情况,只要出现,就从否认的史中去掉
+            if (neg_diags.contains("冠状动脉支架植入术")) {
+                neg_diags.remove("冠状动脉支架植入术");
+            }
+            String neg_diags_first[] = pasttext.split("否认");
+            for (String str1 : neg_diags_first) {
+                String neg_diags_second[] = str1.split("、");
+                for (String str2 : neg_diags_second) {
+                    for (String neg_diag : neg_diags) {
+                        if (str2.contains(neg_diag)) {
+                            String str3 = str2.substring(0, str2.indexOf(neg_diag));
+                            if (str3.contains("其它") || str3.contains("其他")) {
+                                int index = neg_diags.indexOf(neg_diag);
+                                neg_diags.set(index, "其它的");
+                            }
+                        }
+                    }
+                }
+            }
+            //过敏史
+            extract_Allergy(pastLabel.getAllergies(), pos_diags, neg_diags, pastLabel.getText());
+        } else {
+            //例如邵逸夫 台州这种结构化数据,判断高血压 和 糖尿病是否有冲突
+            List<Diag> pastDiags = new ArrayList<>();
+            Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+            String a1 = structureMap.get("高血压");
+            Diag diag = new Diag();
+            diag.setName("高血压");
+            diag.setHospitalDiagName("高血压");
+            if (StringUtils.isNotEmpty(a1) && "否认".equals(a1)) {
+                Negative negative = new Negative();
+                negative.setName("否认");
+                pastDiags.add(diag);
+            } else if (StringUtils.isNotEmpty(a1) && !"否认".equals(a1)) {
+                pastDiags.add(diag);
+            }
+
+            a1 = structureMap.get("糖尿病");
+            diag = new Diag();
+            diag.setName("糖尿病");
+            diag.setHospitalDiagName("糖尿病");
+            if (StringUtils.isNotEmpty(a1) && "否认".equals(a1)) {
+                Negative negative = new Negative();
+                negative.setName("否认");
+                pastDiags.add(diag);
+            } else if (StringUtils.isNotEmpty(a1) && !"否认".equals(a1)) {
+                pastDiags.add(diag);
+            }
+
+            extract_diags(pastDiags, pos_diags, neg_diags);
+        }
+        if (initialDiagLabel != null) {
+            extract_diags(initialDiagLabel.getDiags(), pos_diags, neg_diags);
+        }
+
+        String infoStr = "";
+        int matchSum = 0;
+        ModelAI modelAI = new ModelAI();
+        for (String negdiag : neg_diags) {
+            JSONArray jsonArray = modelAI.loadChiefPresentSimilarAI(negdiag, pos_diags, false
+                    , "diagnose", chiefPresentSimilarityServiceClient);
+            if (jsonArray.size() == 2) {
+                /* 相似度最高症状 */
+                String dgname = jsonArray.getString(0);
+                if ("糖尿病".equals(negdiag) && "妊娠期糖尿病".equals(dgname)) {
+                    continue;
+                }
+                if ("高血压史".equals(negdiag) && "高血压病".equals(dgname)) {
+                    matchSum++;
+                    if (StringUtils.isEmpty(infoStr)) {
+                        infoStr = negdiag;
+                    } else {
+                        infoStr = infoStr + "," + negdiag;
+                    }
+                    continue;
+                }
+                /* 相似度分数 */
+                double likeRate = jsonArray.getDoubleValue(1);
+                if (likeRate > 0.99) {
+                    matchSum++;
+                    if (StringUtils.isEmpty(infoStr)) {
+                        infoStr = negdiag;
+                    } else {
+                        infoStr = infoStr + "," + negdiag;
+                    }
+                    continue;
+                }
+            }
+        }
+        if (matchSum > 0) {
+            status.set("-1");
+            info.set(infoStr);
+        }
+
+    }
+
+    //疾病史
+    private List<String> extract_diags(List<Diag> diags, List<String> pos_diags, List<String> neg_diags) {
+        List<String> dgs = new ArrayList<>();
+        for (Diag dg : diags) {
+            String dgname = dg.getHospitalDiagName();
+            if (dg.getNegative() == null) {
+                if (!pos_diags.contains(dgname)) {
+                    pos_diags.add(dgname);
+                }
+            } else {
+                if (!neg_diags.contains(dgname)) {
+                    neg_diags.add(dgname);
+                    dgs.add(dgname);
+                }
+            }
+        }
+        return dgs;
+    }
+
+    //过敏史
+    private List<String> extract_Allergy(List<Allergy> allergys, List<String> pos_diags, List<String> neg_diags, String text) {
+        List<String> dgs = new ArrayList<>();
+        String content = text;
+        int allergyNum = 0;
+        for (Allergy dg : allergys) {
+            String dgname = dg.getName();
+            if (dg.getNegative() == null) {
+                if (!pos_diags.contains(dgname)) {
+                    pos_diags.add(dgname);
+                }
+                if (dg.getAllergyFood() != null) {
+                    allergyNum = 1;
+                }
+                if (dg.getAllergyMedicine() != null) {
+                    allergyNum = 2;
+                }
+            } else {
+                int index = content.lastIndexOf(dgname);
+                text = content.substring(Math.max(0, index - 10), index);
+                if (allergyNum == 0 && !neg_diags.contains(dgname) && !text.contains("其他") && !text.contains("其它") && !text.contains("其余")) {
+                    neg_diags.add(dgname);
+                    dgs.add(dgname);
+                }
+                if (allergyNum == 1 && text.contains("食物") && !neg_diags.contains(dgname) && !text.contains("其他") && !text.contains("其它") && !text.contains("其余")) {
+                    neg_diags.add(dgname);
+                    dgs.add(dgname);
+                }
+                if (allergyNum == 2 && text.contains("药物") && !neg_diags.contains(dgname) && !text.contains("其他") && !text.contains("其它") && !text.contains("其余")) {
+                    neg_diags.add(dgname);
+                    dgs.add(dgname);
+                }
+            }
+        }
+        return dgs;
+    }
+
+    private boolean isContains(String diagName) {
+        for (String c : containList) {
+            if (diagName.contains(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isFilter(String diagName) {
+        for (String c : filterList) {
+            if (diagName.contains(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}

+ 84 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0372.java

@@ -0,0 +1,84 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.MaritalStatus;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Description: 婚姻状况前后不一致
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0372 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+
+        String basicInfoStatus = inputInfo.getBeHospitalizedDoc().getStructureMap().get("婚育史");
+        String bhbasicInfoStatus = inputInfo.getBeHospitalizedDoc().getStructureMap().get("婚姻状况");
+        if (StringUtil.isBlank(bhbasicInfoStatus)) {
+            bhbasicInfoStatus = inputInfo.getBeHospitalizedDoc().getStructureMap().get("婚姻");
+        }
+        if (StringUtil.isBlank(basicInfoStatus) || StringUtil.isBlank(bhbasicInfoStatus)) {
+            status.set("0");
+            return;
+        }
+
+        if (bhbasicInfoStatus.equals("未婚") && basicInfoStatus.contains("结婚")) {
+            return;
+        }
+
+        if (bhbasicInfoStatus.equals("未婚") && basicInfoStatus.contains("未婚")) {
+            status.set("0");
+            return;
+        }
+
+        if (StringUtil.isNotBlank(bhbasicInfoStatus) && basicInfoStatus.equals("其他")) {
+            status.set("0");
+            return;
+        }
+
+        if (bhbasicInfoStatus.equals("丧偶") &&
+                (basicInfoStatus.contains("已故") || basicInfoStatus.contains("丧偶") || basicInfoStatus.contains("去世") ||
+                        basicInfoStatus.contains("亡故") || basicInfoStatus.contains("已逝"))) {
+            status.set("0");
+            return;
+        }
+
+        if ((bhbasicInfoStatus.equals("离婚") || bhbasicInfoStatus.equals("离异")) &&
+                (basicInfoStatus.contains("离异") || bhbasicInfoStatus.equals("离婚"))) {
+            status.set("0");
+            return;
+        }
+
+        if (bhbasicInfoStatus.equals("已婚") && (basicInfoStatus.contains("结婚") || basicInfoStatus.contains("再婚") || basicInfoStatus.contains("已婚") )) {
+            status.set("0");
+            return;
+        }
+
+        MaritalStatus mts = inputInfo.getBeHospitalizedDoc().getMaritalLabel().getMaritalStatus();
+        if (mts != null && StringUtil.isNotBlank(mts.getName())) {
+            String maritalStatus = mts.getName();
+            if (basicInfoStatus.equals(maritalStatus)) {
+                status.set("0");
+                return;
+            }
+        }
+
+        //无婚育史
+        MaritalLabel maritalLabel = inputInfo.getBeHospitalizedDoc().getMaritalLabel();
+        if (maritalLabel == null || maritalLabel.getText() == null) {
+            status.set("0");
+        }
+    }
+
+}

+ 110 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0375.java

@@ -0,0 +1,110 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.google.common.collect.Lists;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.entity.Family;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 配偶健康状况未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0375 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //硬规则 匹配未婚
+        String marry = inputInfo.getBeHospitalizedDoc().getStructureMap().get(Content.marry);
+        if(StringUtil.isBlank(marry)){
+            marry = inputInfo.getBeHospitalizedDoc().getStructureMap().get("婚姻状况");
+        }
+        MaritalLabel maritalLabel = inputInfo.getBeHospitalizedDoc().getMaritalLabel();
+        if ("未婚".equals(marry) || "丧偶".equals(marry) ||
+                (maritalLabel != null &&
+                (StringUtil.isBlank(maritalLabel.getText())
+                        || maritalLabel.getText().contains("未婚")
+                        || maritalLabel.getText().contains("离婚")
+                        || maritalLabel.getText().contains("离异")
+                        || maritalLabel.getText().contains("已故")
+                        || maritalLabel.getText().contains("亡故")
+                        || maritalLabel.getText().contains("已逝")
+                        || maritalLabel.getText().contains("丧偶")
+                        || maritalLabel.getText().contains("详见原病历")
+                        || maritalLabel.getText().contains("见旧病历")
+                        || maritalLabel.getText().contains("见既往病历")
+                )
+                )
+        ) {
+            status.set("0");
+            return;
+        }
+        //台州市结构化的
+        Map<String, String> structureMap_beh = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap_beh.get("配偶健康状况"))) {
+            status.set("0");
+            return;
+        }
+        List<Family> familyList = Lists.newArrayList();
+        if (inputInfo.getBeHospitalizedDoc().getFamilyLabel() != null){
+            List<Family> familiesFl = inputInfo.getBeHospitalizedDoc().getFamilyLabel().getFamilies();
+            if (ListUtil.isNotEmpty(familiesFl)) {
+                familyList.addAll(familiesFl);
+            }
+        }
+
+        if (inputInfo.getBeHospitalizedDoc().getMaritalLabel() != null){
+            List<Family> familiesMl = inputInfo.getBeHospitalizedDoc().getMaritalLabel().getFamily();
+            if (ListUtil.isNotEmpty(familiesMl)) {
+                familyList.addAll(familiesMl);
+            }
+        }
+        Pattern p = Pattern.compile("[配偶|妻子|爱人]");
+        List<Family> filterFamilies = familyList
+                .stream()
+                .filter(
+                        i -> i != null
+                                && StringUtil.isNotBlank(i.getName())
+                                && p.matcher(i.getName()).find()).collect(Collectors.toList());
+
+        for (Family family : filterFamilies) {
+            if (family.getDead() != null) {
+                status.set("0");
+                return;
+            } else if (family.getHealthCondition() != null && StringUtil.isNotBlank(family.getHealthCondition().getName())) {
+                status.set("0");
+                return;
+            } else if (family.getDiags() != null && family.getDiags().size() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+
+        //特殊情况 配偶健康状况未描述  200702106000049
+        String marrytext=inputInfo.getBeHospitalizedDoc().getMaritalLabel().getText();
+        if (StringUtil.isNotBlank(marrytext)
+                && ((marrytext.contains("爱人") || marrytext.contains("配偶") || marrytext.contains("妻子") || marrytext.contains("丈夫"))
+                || (marrytext.contains("详见原病历") || marrytext.contains("已故") || marrytext.contains("体健") || marrytext.contains("去世")))) {
+            status.set("0");
+            return;
+        }
+    }
+
+}

+ 109 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0376.java

@@ -0,0 +1,109 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.google.common.collect.Lists;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.pub.Content;
+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.entity.Family;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 子女健康状况未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0376 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        MaritalLabel maritalLabel = beHospitalizedDoc.getMaritalLabel();
+        //台州市结构化的
+        Map<String, String> structureMap_beh = beHospitalizedDoc.getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap_beh.get("子女健康状况"))) {
+            status.set("0");
+            return;
+        }
+        if (maritalLabel == null || StringUtils.isEmpty(maritalLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        //硬规则 匹配未育
+        String marry = structureMap_beh.get(Content.marry);
+        if (StringUtil.isBlank(marry)) {
+            marry = structureMap_beh.get("婚姻状况");
+        }
+        if ("未婚".equals(marry)) {
+            status.set("0");
+            return;
+        }
+        String maritalText = maritalLabel.getText();
+        if (StringUtil.isNotBlank(maritalText)) {
+            List<String> words = Lists.newArrayList("未婚", "未育", "未婚育", "未生育", "未生", "0子0女", "详见原病历",
+                    "无子女","见旧病历","见既往病历");
+            for (String word : words) {
+                if (maritalText.contains(word)) {
+                    status.set("0");
+                    return;
+                }
+            }
+            String regex = ".*\\d+-\\d+-\\d+-0.*";
+            if (maritalText.matches(regex)) {
+                status.set("0");
+                return;
+            }
+        }
+        //未婚 无子女
+        if (maritalLabel.getMaritalStatus() != null && maritalLabel.getMaritalStatus().getName().contains("未婚")) {
+            status.set("0");
+            return;
+        }
+        List<Family> familyList = Lists.newArrayList();
+        List<Family> familiesFl = inputInfo.getBeHospitalizedDoc().getFamilyLabel().getFamilies();
+        List<Family> familiesMl = inputInfo.getBeHospitalizedDoc().getMaritalLabel().getFamily();
+        if (ListUtil.isNotEmpty(familiesFl)) {
+            familyList.addAll(familiesFl);
+        }
+        if (ListUtil.isNotEmpty(familiesMl)) {
+            familyList.addAll(familiesMl);
+        }
+
+        Pattern p = Pattern.compile("[儿子女兄弟姐妹]");
+        List<Family> filterFamilies = familyList
+                .stream()
+                .filter(
+                        i -> i != null
+                                && StringUtil.isNotBlank(i.getName())
+                                && p.matcher(i.getName()).find()).collect(Collectors.toList());
+
+        for (Family family : filterFamilies) {
+            if (family.getDead() != null) {
+                status.set("0");
+                return;
+            } else if (family.getHealthCondition() != null && StringUtil.isNotBlank(family.getHealthCondition().getName())) {
+                status.set("0");
+                return;
+            } else if (family.getDiags() != null && family.getDiags().size() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+    }
+
+}

+ 95 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0378.java

@@ -0,0 +1,95 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.google.common.collect.Lists;
+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.entity.Family;
+import com.lantone.qc.pub.model.label.FamilyLabel;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 配偶死亡原因未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0378 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            return;
+        }
+        FamilyLabel familyLabel = inputInfo.getBeHospitalizedDoc().getFamilyLabel();
+        MaritalLabel maritalLabel = inputInfo.getBeHospitalizedDoc().getMaritalLabel();
+
+        if (familyLabel == null || maritalLabel == null) {
+            return;
+        }
+        List<Family> familiesFl = null;
+        List<Family> familiesMl = null;
+
+        if (familyLabel != null) {
+            familiesFl = familyLabel.getFamilies();
+            //规则匹配
+            String familyText = familyLabel.getText();
+            if (StringUtil.isNotBlank(familyText) && familyText.contains("自然死亡")) {
+                status.set("0");
+                return;
+            }
+        }
+        if (maritalLabel != null && maritalLabel.getFamily() != null) {
+            familiesMl = maritalLabel.getFamily();
+            for (Family family : familiesMl) {
+                if (family.getDead() != null && family.getName() != null) {
+                    String maritalText = maritalLabel.getText();
+                    int index1 = maritalText.indexOf(family.getDead().getName());
+                    int index2 = maritalText.indexOf(family.getName());
+                    if (0 < index1 && index1 < index2 && maritalText.substring(index1, index2).contains(",")) {
+                        family.setDead(null);
+                    }
+                }
+            }
+        }
+        List<Family> familyList = Lists.newArrayList();
+
+        if (familiesFl != null && familiesFl.size() > 0) {
+            familyList.addAll(familiesFl);
+        }
+        if (familiesMl != null && familiesMl.size() > 0) {
+            familyList.addAll(familiesMl);
+        }
+
+        familyList = familyList
+                .stream()
+                .filter(
+                        i -> i != null
+                                && StringUtil.isNotBlank(i.getName())
+                                && i.getName().contains("配偶")
+                                && i.getDead() != null
+                                && StringUtil.isNotBlank(i.getDead().getName())
+                )
+                .collect(Collectors.toList());
+
+        if (familyList.size() > 0) {
+            status.set("-1");
+            long count = familyList
+                    .stream()
+                    .filter(
+                            i -> (i.getDead().getDeadReason() != null && StringUtil.isNotBlank(i.getDead().getDeadReason().getName()))
+                                    || (i.getDead().getUnknow() != null && StringUtil.isNotBlank(i.getDead().getUnknow().getName()))
+                    )
+                    .count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+    }
+}

+ 97 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0397.java

@@ -0,0 +1,97 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+import com.google.common.collect.Lists;
+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.BeHospitalizedDoc;
+import com.lantone.qc.pub.model.entity.Family;
+import com.lantone.qc.pub.model.label.FamilyLabel;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 父母健康状况未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0397 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("家庭成员健康情况"))) {
+            status.set("0");
+            return;
+        }
+        FamilyLabel familyLabel = beHospitalizedDoc.getFamilyLabel();
+        MaritalLabel maritalLabel = beHospitalizedDoc.getMaritalLabel();
+
+        String familyText = "", maritalText = "";
+        List<Family> familiesFl = null;
+        List<Family> familiesMl = null;
+        if (familyLabel != null) {
+            familyText = familyLabel.getText();
+            familiesFl = familyLabel.getFamilies();
+        }
+        if (maritalLabel != null && StringUtil.isNotBlank(maritalLabel.getText())) {
+            maritalText = maritalLabel.getText();
+            familiesMl = maritalLabel.getFamily();
+        }
+        if (StringUtil.isNotBlank(familyText)) {
+            familyText = StringUtil.removeBlank(familyText);
+        } else {
+            status.set("0");
+            return;
+        }
+        if ((StringUtil.isNotBlank(familyText)
+                && ((familyText.contains("父") || familyText.contains("双亲") || familyText.contains("母"))
+                && (familyText.contains("详见原病历") || familyText.contains("见旧病历") || familyText.contains("见既往病历") || familyText.contains("已故") || familyText.contains("体健") || familyText.contains("去世") || familyText.contains("健康")))
+                || (StringUtil.isNotBlank(maritalText) && ((maritalText.contains("父") || maritalText.contains("双亲") || maritalText.contains("母"))
+                && (maritalText.contains("详见原病历") || maritalText.contains("见旧病历") || maritalText.contains("见既往病历") || maritalText.contains("已故") || maritalText.contains("体健") || maritalText.contains("去世") || familyText.contains("健康")))))) {
+            status.set("0");
+            return;
+        }
+        List<Family> familyList = Lists.newArrayList();
+        if (familiesFl != null && familiesFl.size() > 0) {
+            familyList.addAll(familiesFl);
+        }
+        if (familiesMl != null && familiesMl.size() > 0) {
+            familyList.addAll(familiesMl);
+        }
+
+        Pattern p = Pattern.compile("[父母爸妈]");
+        List<Family> filterFamilies = familyList
+                .stream()
+                .filter(
+                        i -> i != null
+                                && StringUtil.isNotBlank(i.getName())
+                                && p.matcher(i.getName()).find()).collect(Collectors.toList());
+        for (Family family : filterFamilies) {
+            if (family.getDead() != null) {
+                status.set("0");
+                return;
+            } else if (family.getHealthCondition() != null && StringUtil.isNotBlank(family.getHealthCondition().getName())) {
+                status.set("0");
+                return;
+            } else if (family.getDiags() != null && family.getDiags().size() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+    }
+
+}

+ 47 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0439.java

@@ -0,0 +1,47 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.util.StringUtil;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+
+/**
+ * @ClassName : BEH0439
+ * @Description : 入院记录个人出生日期填写错误
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class BEH0439 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getBeHospitalizedDoc() == null || inputInfo.getFirstPageRecordDoc() == null) {
+            return;
+        }
+        Map<String, String> bhMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        Map<String, String> fprMap = inputInfo.getFirstPageRecordDoc().getStructureMap();
+        if (bhMap != null && fprMap != null) {
+            String birthDate_bh = bhMap.get("出生日期");
+            String birthDate_fpr = fprMap.get("出生日期");
+            String[] dateFormats = new String[]{"yyyy年MM月dd日", "yyyy-MM-dd"};
+            if (birthDate_bh != null && birthDate_fpr != null) {
+                if (!CatalogueUtil.isEmpty(birthDate_bh) && !CatalogueUtil.isEmpty(birthDate_fpr)) {
+                    if(!DateUtils.isSameInstant(StringUtil.parseDateTime(birthDate_bh, dateFormats)
+                            ,StringUtil.parseDateTime(birthDate_fpr, dateFormats)))
+                    {
+                        status.set("-1");
+                    }
+//                    if (!DateUtil.format(DateUtil.parseDate(birthDate_bh), DateUtil.DATE_FORMAT).equals(DateUtil.format(DateUtil.parseDate(birthDate_fpr), DateUtil.DATE_FORMAT))) {
+//                        status.set("-1");
+//                    }
+                }
+            }
+        }
+    }
+}

+ 55 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/behospitalized/BEH0454.java

@@ -0,0 +1,55 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.behospitalized;
+
+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.entity.Diag;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * @ClassName : BEH0454
+ * @Description :  既往史未描述既往健康状况
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class BEH0454 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("既往健康状况"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        if (StringUtils.isNotEmpty(pastLabel.getHeathCondition()) || pastLabel.getText().contains("既往")
+                || pastLabel.getText().contains("详见原病历") || pastLabel.getText().contains("健康状况")
+                || pastLabel.getText().contains("见旧病历")) {
+            status.set("0");
+            return;
+        }
+        //既往有任一阳性疾病名称,则认为有健康状况
+        List<Diag> diags = pastLabel.getDiags();
+        for (Diag diag : diags) {
+            if (diag.getNegative() == null) {
+                status.set("0");
+                return;
+            }
+        }
+    }
+}

+ 68 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/consultation/CON0281.java

@@ -0,0 +1,68 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.consultation;
+
+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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationResultsDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : CON0281
+ * @Description : 急会诊未在十分钟内到达
+ * @Author : 王宇
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class CON0281 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getConsultationDocs() == null) {
+            return;
+        }
+        //会诊单按日会诊申请日期排序
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
+        //医嘱按照医嘱开始时间排序
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        Collections.sort(doctorAdviceDocs, new Comparator<DoctorAdviceDoc>() {
+            public int compare(DoctorAdviceDoc o1, DoctorAdviceDoc o2) {
+                return o1.getStructureMap().get("医嘱开始时间")
+                        .compareTo(o2.getStructureMap().get("医嘱开始时间"));
+            }
+        });
+        for (ConsultationDoc consultationDoc : consultationDocs) {
+            ConsultationResultsDoc consultationResultsDoc = consultationDoc.getConsultationResultsDoc();
+            if (consultationResultsDoc == null) {
+                continue;
+            }
+            Map<String, String> conStructureMap = consultationResultsDoc.getStructureMap();
+            if (conStructureMap.get("会诊类别") == null || !conStructureMap.get("会诊类别").contains("急会诊")) {
+                continue;
+            }
+            for (DoctorAdviceDoc doctorAdviceDoc : doctorAdviceDocs) {//循环取最近一条医嘱和会诊申请单中的急会诊比较
+                Map<String, String> docStructureMap = doctorAdviceDoc.getStructureMap();
+                if (docStructureMap.get("医嘱项目名称") != null && docStructureMap.get("医嘱项目名称").contains("会诊")) {
+                    String applicationDateStr = docStructureMap.get("医嘱开始时间");
+                    String arrivalDateStr = conStructureMap.get("会诊时间");
+                    if (StringUtil.isBlank(applicationDateStr) || StringUtil.isBlank(arrivalDateStr)) {
+                        continue;
+                    }
+                    Date applicationDate = StringUtil.parseDateTime(applicationDateStr);//医嘱开始时间
+                    Date arrivalDate = StringUtil.parseDateTime(arrivalDateStr);//会诊时间
+                    if (applicationDate == null || arrivalDate == null) {
+                        continue;
+                    }
+                    if (CatalogueUtil.compareTime(applicationDate, arrivalDate, 10L)) {
+                        status.set("-1");
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}

+ 106 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/consultation/CON0526.java

@@ -0,0 +1,106 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.consultation;
+
+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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationResultsDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.*;
+
+/**
+ * @ClassName : CON0526
+ * @Description : 急会诊未在10分钟内到达
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class CON0526 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        if (inputInfo.getConsultationDocs().size() == 0) {
+            return;
+        }
+        //会诊单按日会诊申请日期排序
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
+        //医嘱按照医嘱开始时间排序
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        Collections.sort(doctorAdviceDocs, new Comparator<DoctorAdviceDoc>() {
+            public int compare(DoctorAdviceDoc o1, DoctorAdviceDoc o2) {
+                return o1.getStructureMap().get("医嘱开始时间")
+                        .compareTo(o2.getStructureMap().get("医嘱开始时间"));
+            }
+        });
+        for (ConsultationDoc consultationDoc : consultationDocs) {
+            ConsultationResultsDoc consultationResultsDoc = consultationDoc.getConsultationResultsDoc();
+            if (consultationResultsDoc == null) {
+                continue;
+            }
+            Map<String, String> conStructureMap = consultationResultsDoc.getStructureMap();
+            if (conStructureMap.get("会诊类别") == null || !conStructureMap.get("会诊类别").contains("急会诊")) {
+                continue;
+            }
+            for (DoctorAdviceDoc doctorAdviceDoc : doctorAdviceDocs) {//循环取最近一条医嘱和会诊申请单中的急会诊比较
+                Map<String, String> docStructureMap = doctorAdviceDoc.getStructureMap();
+                if (docStructureMap.get("医嘱项目名称") != null && docStructureMap.get("医嘱项目名称").contains("会诊")) {
+                    String applicationDateStr = docStructureMap.get("医嘱开始时间");
+                    String arrivalDateStr = conStructureMap.get("会诊时间");
+                    if (StringUtil.isBlank(applicationDateStr) || StringUtil.isBlank(arrivalDateStr)) {
+                        continue;
+                    }
+                    Date applicationDate = StringUtil.parseDateTime(applicationDateStr);//医嘱开始时间
+                    Date arrivalDate = StringUtil.parseDateTime(arrivalDateStr);//会诊时间
+                    if (applicationDate == null || arrivalDate == null) {
+                        continue;
+                    }
+                    if (CatalogueUtil.compareTime(applicationDate, arrivalDate, 10L)) {
+                        status.set("-1");
+                        return;
+                    }
+                }
+            }
+        }
+
+
+
+
+        
+
+        /*status.set("0");
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
+        if (consultationDocs.size() > 0) {
+            for (ConsultationDoc consulation : consultationDocs) {
+                ConsultationApplicationDoc consultationApplicationDoc = consulation.getConsultationApplicationDoc();
+                ConsultationResultsDoc consultationResultsDoc = consulation.getConsultationResultsDoc();
+                Map<String, String> applicationMap = consultationApplicationDoc.getStructureMap();
+                Map<String, String> resultMap = consultationResultsDoc.getStructureMap();
+                if (applicationMap != null && resultMap != null) {
+                    String applicationDate = applicationMap.get("申请日期");
+                    String resultDate = resultMap.get("会诊到达时间");
+                    if (applicationDate != null && resultDate != null) {
+                        Date date_in = StringUtil.parseDateTime(applicationDate);
+                        Date date_out = StringUtil.parseDateTime(resultDate);
+                        if (date_in == null || date_out == null) {
+                            return;
+                        }
+                        long times = date_out.getTime() - date_in.getTime();
+                        long day = times / (24 * 60 * 60 * 1000);
+                        long hour = (times / (60 * 60 * 1000) - day * 24);
+                        long min = ((times / (60 * 1000)) - day * 24 * 60 - hour * 60);
+                        long min1 = ((times / (60 * 1000)) - day * 24 * 60 - hour * 60);
+                        if (min1 > 10) {
+                            status.set("-1");
+                        }
+
+                    }
+                }
+            }
+        }*/
+
+    }
+}

+ 117 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/crisisvaluereport/CRI0382.java

@@ -0,0 +1,117 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.crisisvaluereport;
+
+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.CrisisInfoDoc;
+import com.lantone.qc.pub.model.doc.CrisisValueReportDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Description: 接到危急值报告后6小时内无病程记录
+ * @author: rengb
+ * @time: 2020/3/19 19:54
+ */
+@Component
+public class CRI0382 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //无危急值结构化信息
+        List<CrisisInfoDoc> crisisInfoDocs = inputInfo.getCrisisInfoDocs();
+        if (crisisInfoDocs == null || crisisInfoDocs.size() == 0) {
+            return;
+        }
+        //有结构化信息但无危急值文书,如果报告时间都未超过6小时 允许无文书
+        boolean isOutTime = false;
+        Date currentDate = new Date();
+        int timeCha = 21600000;
+        String[] dateFormats = new String[]{"yyyy年MM月dd日HH时mm分", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm"};
+        for (CrisisInfoDoc crisisInfoDoc : crisisInfoDocs) {
+            String reptTime = crisisInfoDoc.getStructureMap().get("报告时间");
+            if (StringUtils.isNotEmpty(reptTime)) {
+                if (currentDate.getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime() > timeCha) {
+                    isOutTime = true;
+                }
+            }
+        }
+        ;
+        if (isOutTime == false) {   //所有文书都未超过6小时,规则直接通过
+            return;
+        }
+
+        List<CrisisValueReportDoc> crisisValueReportDocs = inputInfo.getCrisisValueReportDocs();
+        //如果有危急值结构化数据 但无文书则直接提醒无危急值报告
+        if (crisisValueReportDocs == null || crisisValueReportDocs.size() == 0) {
+            status.set("-1");
+            return;
+        }
+        List<String> findCrisises = new ArrayList<>();
+        List<String> allCrisises = new ArrayList<>();
+        crisisInfoDocs.forEach(crisisInfoDoc -> {
+            String reptTime = crisisInfoDoc.getStructureMap().get("报告时间");
+            String crisisName = crisisInfoDoc.getStructureMap().get("危急结果值");
+            String crisisNm = "";
+            String companyNum = "";
+            if (crisisName.contains("项目为")) {
+                crisisName = crisisName.substring(crisisName.indexOf("项目为") + 3);
+            }
+            if (crisisName.contains(",结果:")) {
+                crisisNm = crisisName.split(",结果:")[0];
+                companyNum = crisisName.split(",结果:")[1];
+                if (StringUtil.isNotBlank(companyNum) && companyNum.contains("单位:")) {
+                    companyNum = companyNum.replaceAll("单位:", "");
+                }
+            }
+            allCrisises.add(reptTime);
+            if (StringUtils.isNotEmpty(reptTime)) {
+                //当前时间和报告时间未超过6小时,规则通过
+                if (currentDate.getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime() < timeCha) {
+                    findCrisises.add(reptTime);
+                } else {
+                    for (CrisisValueReportDoc crisisValueReportDoc : crisisValueReportDocs) {
+                        String recordTimeStr = crisisValueReportDoc.getStructureMap().get("病历日期");
+                        String docReptContent = crisisValueReportDoc.getStructureMap().get("病情分析及处理");
+                        if ((StringUtil.parseDateTime(recordTimeStr, dateFormats).getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime()) < timeCha
+                                && (StringUtil.removeBlank(docReptContent).contains(StringUtil.removeBlank(crisisName)) ||
+                                (StringUtil.isNotBlank(crisisNm) && StringUtil.isNotBlank(companyNum)
+                                        && StringUtil.removeBlank(docReptContent).contains(StringUtil.removeBlank(crisisNm))
+                                        && StringUtil.removeBlank(docReptContent).contains(StringUtil.removeBlank(companyNum))
+                                ))) {
+                            findCrisises.add(reptTime);
+                            break;
+                        }
+                    }
+                }
+            }
+        });
+        // (StringUtil.parseDateTime(recordTimeStr, dateFormats).getTime() - StringUtil.parseDateTime(reptTime, dateFormats).getTime()) > 0
+        //                                &&
+        if (findCrisises.size() != allCrisises.size()) {
+            status.set("-1");
+            allCrisises.forEach(reptTime -> {
+                String criticalValueName = "";
+                if (!findCrisises.contains(reptTime) && !info.get().contains(reptTime)) {
+                    for (CrisisInfoDoc crisisInfoDoc : crisisInfoDocs) {
+                        if (reptTime.equals(crisisInfoDoc.getStructureMap().get("报告时间"))) {
+                            criticalValueName = crisisInfoDoc.getStructureMap().get("危急值名称");
+                        }
+                    }
+                    if (StringUtils.isEmpty(info.get())) {
+                        info.set(reptTime + " 危急值名称(" + criticalValueName + ")");
+                    } else {
+                        info.set(info.get() + "; " + reptTime + " 危急值名称(" + criticalValueName + ")");
+                    }
+                }
+            });
+        }
+    }
+
+}

+ 212 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/dutyshiftsystem/DUT0296.java

@@ -0,0 +1,212 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.dutyshiftsystem;
+
+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.LeaveHospitalDoc;
+import com.lantone.qc.pub.model.doc.StagesSummaryDoc;
+import com.lantone.qc.pub.model.doc.transferrecord.TransferRecordDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : DUT0296
+ * @Description : 无阶段小结
+ * 住院时间超过1个月需要阶段小结, 出院时间和入院时间做比较,超过一个月查找阶段小结
+ * 住院30天患者需写“阶段小结”,但在住院期间转科的患者,时间应该是从转科那天算起。
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class DUT0296 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (leaveHospitalDoc == null) {
+            return;
+        }
+
+        List<StagesSummaryDoc> stagesSummaryDocs = inputInfo.getStagesSummaryDocs();
+        TransferRecordDoc transferRecordDoc = inputInfo.getTransferRecordDocs();//转科记录
+        Map<String, String> structureMap_leave = leaveHospitalDoc.getStructureMap();
+        int lengthOfStayNum = 0;
+        String beDate = null, leaveDate = null;
+        if (inputInfo.getMedicalRecordInfoDoc() != null) {
+            Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+            beDate = medicalRecordInfoStructureMap.get("behospitalDate");
+            leaveDate = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+            /* 住院天数小于30天不报错*/
+            if (StringUtil.isNotBlank(beDate) && StringUtil.isNotBlank(leaveDate)) {
+                lengthOfStayNum = dateDifference(beDate, leaveDate);
+                if (lengthOfStayNum < 30) {
+                    return;
+                }
+            }
+        }
+
+        lengthOfStayNum = getLengthOfStay(structureMap_leave);
+        if (lengthOfStayNum < 30) {
+            return;
+        }
+        if (transferRecordDoc != null) {
+            List<TransferRecordDoc> allTransferDocs = transferRecordDoc.getAllTransferDocs();
+            String firstOutRecordDate = getFirstOutRecordDate(allTransferDocs);
+            if (StringUtil.isNotBlank(firstOutRecordDate) && StringUtil.isNotBlank(beDate)) {
+                //入院时间到第一次转出时间,大于30天则报错
+                int dateDifference = dateDifference(beDate, firstOutRecordDate);
+                if (dateDifference > 30 && (stagesSummaryDocs == null || stagesSummaryDocs.size() == 0)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+            String lastInrecordDate = getLastInrecordDate(allTransferDocs);
+            if (StringUtil.isNotBlank(lastInrecordDate) && StringUtil.isNotBlank(leaveDate)) {
+                //最后一次转入时间到出院时间,大于30天则报错
+                int dateDifference = dateDifference(lastInrecordDate, leaveDate);
+                if (dateDifference > 30 && (stagesSummaryDocs == null || stagesSummaryDocs.size() == 0)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+            List<String> allRecordPairs = getAllRecordPairs(allTransferDocs);
+            for (String recordPair : allRecordPairs) {
+                String[] recordPairSplit = recordPair.split(",");
+                int dateDifference = dateDifference(recordPairSplit[0], recordPairSplit[1]);
+                if (dateDifference > 30 && (stagesSummaryDocs == null || stagesSummaryDocs.size() == 0)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        } else {
+            //没有转科记录,直接入院时间和出院时间比
+            if (StringUtil.isNotBlank(beDate) && StringUtil.isNotBlank(leaveDate)) {
+                int dateDifference = dateDifference(beDate, leaveDate);
+                if (dateDifference > 30 && (stagesSummaryDocs == null || stagesSummaryDocs.size() == 0)) {
+                    status.set("-1");
+                }
+                if (checkDays(stagesSummaryDocs, dateDifference)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+
+            int lengthOfStay = getLengthOfStay(structureMap_leave);
+            if (checkDays(stagesSummaryDocs, lengthOfStay)) {
+                status.set("-1");
+            }
+        }
+
+    }
+
+    private int getLengthOfStay(Map<String, String> structureMap_leave) {
+        int lengthOfStayNum = 0;
+        String lengthOfStay = structureMap_leave.get("住院天数");
+        if (StringUtil.isNotBlank(lengthOfStay)) {
+            lengthOfStayNum = onlyNum(lengthOfStay);
+        }
+        return lengthOfStayNum;
+    }
+
+    private boolean checkDays(List<StagesSummaryDoc> stagesSummaryDocs, int dateDifference) {
+        if (stagesSummaryDocs != null && stagesSummaryDocs.size() > 0) {
+            int sum = dateDifference / 30;
+            return sum > stagesSummaryDocs.size();
+        }
+        return true;
+    }
+
+    /**
+     * 获取第一条转出记录时间
+     *
+     * @param allTransferDocs
+     * @return
+     */
+    private String getFirstOutRecordDate(List<TransferRecordDoc> allTransferDocs) {
+        for (TransferRecordDoc transferDoc : allTransferDocs) {
+            Map<String, String> structureMap = transferDoc.getStructureMap();
+            String outDepartment = structureMap.get("转出科室");
+            if (StringUtil.isBlank(outDepartment)) {
+                //如果转出科室为空,则为转出记录
+                return structureMap.get("转科日期");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取最后一条转入记录
+     *
+     * @param allTransferDocs
+     * @return
+     */
+    private String getLastInrecordDate(List<TransferRecordDoc> allTransferDocs) {
+        for (int i = allTransferDocs.size() - 1; i > 0; i--) {
+            Map<String, String> structureMap = allTransferDocs.get(i).getStructureMap();
+            String outDepartment = structureMap.get("转出科室");
+            if (StringUtil.isNotBlank(outDepartment)) {
+                //如果转出科室不为空,则为转入记录
+                return structureMap.get("转科日期");
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 获取所有转入时间-转出时间 pair
+     *
+     * @param allTransferDocs
+     * @return
+     */
+    private List<String> getAllRecordPairs(List<TransferRecordDoc> allTransferDocs) {
+        List<String> allRecordPairs = new ArrayList<>();
+        for (int i = 0; i < allTransferDocs.size(); i++) {
+            Map<String, String> structureMap = allTransferDocs.get(i).getStructureMap();
+            String outDepartment = structureMap.get("转出科室");
+            if (StringUtil.isNotBlank(outDepartment)) {
+                //如果转出科室不为空,则为转入记录
+                String transferDateInStr = structureMap.get("转科日期");
+                if (i + 1 < allTransferDocs.size()) {
+                    //如果转入记录下一条是转出记录
+                    Map<String, String> nextStructureMap = allTransferDocs.get(i + 1).getStructureMap();
+                    if (StringUtil.isBlank(nextStructureMap.get("转出科室"))) {
+                        //如果转出科室为空,则为转出记录
+                        String transferDateOutStr = nextStructureMap.get("转科日期");
+                        allRecordPairs.add(transferDateInStr + "," + transferDateOutStr);
+                    }
+                }
+            }
+        }
+        return allRecordPairs;
+    }
+
+    private int dateDifference(String beDate, String leaveDate) {
+        int day = 0;
+        try {
+            Date dateIn = StringUtil.parseDateTime(beDate);
+            Date dateOut = StringUtil.parseDateTime(leaveDate);
+            if (dateIn == null || dateOut == null) return 0;
+            Calendar from = Calendar.getInstance();
+            from.setTime(dateIn);
+            Calendar to = Calendar.getInstance();
+            to.setTime(dateOut);
+            long timeS,timeE,timeDiff = 0;
+            try {
+                timeS = from.getTimeInMillis();
+                timeE = to.getTimeInMillis();
+                timeDiff = (timeE - timeS) / (1000 * 60* 60 * 24);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            day = (int) timeDiff;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return day;
+    }
+
+    private int onlyNum(String str) {
+        return Integer.parseInt(str.replaceAll("[^0-9]", ""));
+    }
+}

+ 91 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/dutyshiftsystem/DUT0598.java

@@ -0,0 +1,91 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.dutyshiftsystem;
+
+import com.google.common.collect.Lists;
+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.transferrecord.TransferIntoDoc;
+import com.lantone.qc.pub.model.doc.transferrecord.TransferRecordDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : DUT0598
+ * @Description : 接科记录未在24h内完成
+ * @Author : 胡敬
+ * @Date: 2020-03-30 15:06
+ */
+@Component
+public class DUT0598 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        TransferRecordDoc transferRecordDoc = inputInfo.getTransferRecordDocs();
+        if (transferRecordDoc == null) {
+            return;
+        }
+        List<TransferIntoDoc> transferIntoDocs = transferRecordDoc.getTransferIntoDocs();
+        String rollInRecordDateStr = "", recordDateStr = "", removeDate = "";
+        Date rollOutRecordDate = null, recordDate = null;
+        if (transferIntoDocs == null || transferIntoDocs.get(0).getStructureMap() == null) {
+            return;
+        }
+        //转入时间
+        rollInRecordDateStr = transferIntoDocs.get(0).getStructureMap().get("转入时间");
+        if (StringUtil.isBlank(rollInRecordDateStr)) {
+            return;
+        }
+        String[] split = rollInRecordDateStr.replace("[", "").replace("]", "").trim().split(",");
+//        List<String> dateLists = Arrays.asList(split);
+        List<String> dateLists = Lists.newArrayList();
+        for (String date : split) {
+            dateLists.add(date);
+        }
+        dateLists = dateLists.stream().sorted().collect(Collectors.toList());
+        for (TransferIntoDoc transferIntoDoc : transferIntoDocs) {
+            Map<String, Date> maps = new HashMap<>();
+            Map<String, String> transferOutStructureMap = transferIntoDoc.getStructureMap();
+            recordDateStr = transferOutStructureMap.get("病历日期");
+            for (String date : dateLists) {
+                if (CatalogueUtil.isEmpty(date) || CatalogueUtil.isEmpty(recordDateStr)) {
+                    continue;
+                }
+                //转入日期
+                rollOutRecordDate = StringUtil.parseDateTime(date);
+                //记录日期
+                recordDate = StringUtil.parseDateTime(recordDateStr);
+                if (rollOutRecordDate != null && recordDate != null &&
+                        (rollOutRecordDate.before(recordDate) || DateUtils.isSameDay(rollOutRecordDate, recordDate))) {
+                    if (maps.size() == 0) {
+                        removeDate = date;
+                        maps.put("转入时间", rollOutRecordDate);
+                        maps.put("记录时间", recordDate);
+                    } else {
+                        if (recordDate.getTime() - rollOutRecordDate.getTime() < maps.get("记录时间").getTime() - maps.get("转入时间").getTime()) {
+                            removeDate = date;
+                            maps.put("转入时间", rollOutRecordDate);
+                        }
+                    }
+                }
+            }
+
+            if (maps.size() != 0) {
+                boolean compareTime = CatalogueUtil.compareTime(maps.get("转入时间"), maps.get("记录时间"), (long) 24 * 60);
+                boolean sameDay = DateUtils.isSameDay(maps.get("转入时间"), maps.get("记录时间"));
+                dateLists.remove(removeDate);
+                if (compareTime && !sameDay) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+
+}

+ 201 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/firstcourserecord/FIRC0095.java

@@ -0,0 +1,201 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.firstcourserecord;
+
+import com.alibaba.fastjson.JSONArray;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.client.ChiefPresentSimilarityServiceClient;
+import com.lantone.qc.kernel.structure.ai.ModelAI;
+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.entity.Diag;
+import com.lantone.qc.pub.model.entity.GeneralDesc;
+import com.lantone.qc.pub.model.label.DiagLabel;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.model.label.PresentLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : FIRC0095
+ * @Description : 初步诊断不完整
+ * 和入院记录的初步诊断比较
+ * @Author : 楼辉荣
+ * @Date: 2020-03-06 17:28
+ */
+@Component
+public class FIRC0095 extends QCCatalogue {
+    @Autowired
+    ChiefPresentSimilarityServiceClient chiefPresentSimilarityServiceClient;
+
+    private List<String> containList = Arrays.asList("脑萎缩", "慢性", "纤颤", "高血压", "糖尿", "冠状", "冠心病", "支架", "起搏器", "房颤", "风湿");
+    private List<String> filterList = Arrays.asList("心脏病", "低血糖", "急性", ";");
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getFirstCourseRecordDoc() == null || inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        String age = structureMap.get("年龄");
+        if (StringUtil.isNotBlank(age)) {
+            if (age.contains("月") || age.contains("天")) {
+                status.set("0");
+                return;
+            }
+            age = age.replaceAll("岁", "");
+            try {
+                if (Integer.parseInt(age) < 10) {
+                    status.set("0");
+                    return;
+                }
+            } catch (Exception e) {
+                System.out.println("FIRC0095--解析age出错:" + "->" + age);
+            }
+        }
+        PresentLabel presentLabel = inputInfo.getBeHospitalizedDoc().getPresentLabel();
+        DiagLabel initialDiagLabel = inputInfo.getFirstCourseRecordDoc().getInitialDiagLabel();
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        if (initialDiagLabel == null) {
+            status.set("0");
+            return;
+        }
+        Set<String> presentPastDiags = new HashSet<>();
+        if (presentLabel != null) {
+            List<GeneralDesc> generals = presentLabel.getGenerals();
+            if (generals.size() > 0) {
+                String presentText = presentLabel.getText();
+                List<Diag> presentDiags = presentLabel.getDiags();
+                /* 取现病史中一般情况之后的疾病名称 */
+                if (StringUtil.isNotBlank(presentText) && presentDiags.size() > 0) {
+                    String lastGeneral = generals.get(generals.size() - 1).getName();
+                    int lastGeneralIndex = presentText.indexOf(lastGeneral);
+                    for (Diag presentDiag : presentDiags) {
+                        if (presentDiag.getNegative() != null || presentDiag.getHospitalDiagName().contains("否认")) {
+                            continue;
+                        }
+                        /* 现病史中一般情况之后的疾病名称 */
+                        if (presentText.indexOf(presentDiag.getHospitalDiagName()) > lastGeneralIndex) {
+                            if (isContains(presentDiag.getHospitalDiagName()) && !isFilter(presentDiag.getHospitalDiagName())) {
+                                presentPastDiags.add(presentDiag.getHospitalDiagName());
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        /* 取既往史中疾病名称 */
+        if (pastLabel != null && StringUtils.isNotEmpty(pastLabel.getText())) {
+            List<Diag> pastDiags = pastLabel.getDiags();
+            addDiagHospitalName(presentPastDiags, pastDiags);
+        } else {            //结构化数据
+            List<Diag> pastDiags = new ArrayList<>();
+//            Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+            String a1 = structureMap.get("高血压");
+            if (StringUtils.isNotEmpty(a1) && !"否认".equals(a1)) {
+                Diag diag = new Diag();
+                diag.setName("高血压");
+                diag.setHospitalDiagName("高血压");
+                if (!isHave(presentPastDiags, "高血压")) {
+                    pastDiags.add(diag);
+                }
+            }
+            a1 = structureMap.get("糖尿病");
+            if (StringUtils.isNotEmpty(a1) && !"否认".equals(a1)) {
+                Diag diag = new Diag();
+                diag.setName("糖尿病");
+                diag.setHospitalDiagName("糖尿病");
+                if (!isHave(presentPastDiags, "糖尿病")) {
+                    pastDiags.add(diag);
+                }
+            }
+            addDiagHospitalName(presentPastDiags, pastDiags);
+        }
+        /* 取初步诊断中疾病名称 */
+        List<String> initDiags = new ArrayList<>();
+        List<Diag> initialDiagDiags = initialDiagLabel.getDiags();
+        addInitDiagHospitalName(initDiags, initialDiagDiags);
+        initDiags = initDiags.stream().distinct().collect(Collectors.toList());
+
+        String infoStr = "";
+        int matchSum = 0;
+        ModelAI modelAI = new ModelAI();
+        for (String presentPastDiag : presentPastDiags) {
+            JSONArray jsonArray = modelAI.loadChiefPresentSimilarAI(presentPastDiag, initDiags, false
+                    , "diagnose", chiefPresentSimilarityServiceClient);
+            if (jsonArray.size() == 2) {
+                /* 相似度最高症状 */
+                String symptom = jsonArray.getString(0);
+                /* 相似度分数 */
+                double likeRate = jsonArray.getDoubleValue(1);
+                if (likeRate > 0.80) {
+                    matchSum++;
+                } else {
+                    infoStr = CatalogueUtil.concatInfo(infoStr, presentPastDiag);
+                }
+            } else {
+                infoStr = CatalogueUtil.concatInfo(infoStr, presentPastDiag);
+            }
+        }
+        info.set(infoStr);
+        if (matchSum == presentPastDiags.size()) {
+            status.set("0");
+        }
+        /*
+        if (initDiags.containsAll(presentPastDiags)) {
+            status.set("0");
+        }
+         */
+    }
+
+    private void addDiagHospitalName(Set<String> presentPastDiag, List<Diag> pastDiags) {
+        for (Diag pastDiag : pastDiags) {
+            if (pastDiag.getNegative() != null) {
+                continue;
+            }
+            if (isContains(pastDiag.getHospitalDiagName()) && !isFilter(pastDiag.getHospitalDiagName())) {
+                presentPastDiag.add(pastDiag.getHospitalDiagName());
+            }
+        }
+    }
+
+    private void addInitDiagHospitalName(List<String> presentPastDiag, List<Diag> pastDiags) {
+        for (Diag pastDiag : pastDiags) {
+            if (pastDiag.getNegative() != null) {
+                continue;
+            }
+            presentPastDiag.add(pastDiag.getHospitalDiagName());
+        }
+    }
+
+    private boolean isHave(Set<String> presentDiag, String diagName) {
+        for (String haveDiag : presentDiag) {
+            if (haveDiag.contains(diagName) || diagName.contains(haveDiag)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isContains(String diagName) {
+        for (String c : containList) {
+            if (diagName.contains(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isFilter(String diagName) {
+        for (String c : filterList) {
+            if (diagName.contains(c)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}

+ 40 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/firstpagerecord/FIRP02972.java

@@ -0,0 +1,40 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.firstpagerecord;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @ClassName : FIRP02972
+ * @Description : 现住址要具体到门牌号
+ * @Author : Mark
+ * @Date: 2020-06-19 10:23
+ */
+@Component
+public class FIRP02972 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getFirstPageRecordDoc() != null && inputInfo.getFirstPageRecordDoc().getStructureMap() != null) {
+            Map<String, String> firstpageStructureMap = inputInfo.getFirstPageRecordDoc().getStructureMap();
+            String address = firstpageStructureMap.get(Content.current_address);
+            if (!CatalogueUtil.isEmpty(address)) {
+                String suffix = (address.length() <= 5) ? address : (address.substring(address.length() - 5));
+                if (suffix.contains("村")) {
+                    return;
+                }
+                Pattern p = Pattern.compile("[0-9一二三四五六七八九0123456789]");
+                Matcher m = p.matcher(suffix);
+                if (!m.find()) {
+                    status.set("-1");
+                }
+            }
+        }
+    }
+}

+ 63 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/leavehospital/LEA02901.java

@@ -0,0 +1,63 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.leavehospital;
+
+import com.google.common.collect.Lists;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.RegularUtil;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DeathRecordDoc;
+import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.LeaveHospitalDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 出院医嘱记录不规范
+ * @author: 胡敬
+ * @time: 2020/05/27 19:13
+ */
+@Component
+public class LEA02901 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        //医嘱信息
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        if (leaveHospitalDoc == null || doctorAdviceDocs.size() == 0) {
+            return;
+        }
+        //医嘱中无出院带药不触发
+        long count = doctorAdviceDocs.stream().map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("医嘱类型判别")) && x.get("医嘱类型判别").equals("出院带药")).count();
+        if (count == 0) {
+            return;
+        }
+        DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+        if (deathRecordDoc == null || deathRecordDoc.getText() == null) {
+            Map<String, String> structureMap = leaveHospitalDoc.getStructureMap();
+            String dischargeOrder = structureMap.get("出院医嘱");
+            if (StringUtils.isNotEmpty(dischargeOrder)) {
+                //跟医学部任燕青确认过, 去除括号里的东西
+                dischargeOrder = RegularUtil.ClearBracket(dischargeOrder);
+                if (StringUtil.isNotBlank(dischargeOrder)) {
+                    List<String> words = Lists.newArrayList("qd", "bid", "tid", "qid", "qh", "q2h", "q4h", "q6h", "q8h",
+                            "qn", "q3w", "qod", "biw", "qw", "prn", "sos", "lib", "st", "stat", "hs", "am", "po", "ID", "IH", "IM",
+                            "IV", "OD", "OS", "OU", "ivgtt", "Q12H", "QN", "ONCE");
+                    for (String word : words) {
+                        if (dischargeOrder.contains(word.toUpperCase()) || dischargeOrder.contains(word.toLowerCase())) {
+                            status.set("-1");
+                            info.set("出院医嘱用法用量不规范");
+                            return;
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 82 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/leavehospital/LEA02987.java

@@ -0,0 +1,82 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.leavehospital;
+
+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.DeathCaseDiscussDoc;
+import com.lantone.qc.pub.model.doc.DeathRecordDoc;
+import com.lantone.qc.pub.model.doc.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.LeaveHospitalDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : LEA02987
+ * @Description : 出院带药与医嘱不一致
+ * @Author : 胡敬
+ * @Date: 2020-06-23 10:54
+ */
+@Component
+public class LEA02987 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.size() == 0 || leaveHospitalDoc == null) {
+            return;
+        }
+        DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+        DeathCaseDiscussDoc deathCaseDiscussDoc = inputInfo.getDeathCaseDiscussDoc();
+        if (deathRecordDoc != null || deathCaseDiscussDoc != null) {
+            return;
+        }
+        Map<String, String> structureMap = leaveHospitalDoc.getStructureMap();
+        String dischargeOrder = structureMap.get("出院医嘱");
+        if (StringUtil.isBlank(dischargeOrder)) {
+            return;
+        }
+        Set<String> drugs = new HashSet<>();
+        for (DoctorAdviceDoc adviceDoc : doctorAdviceDocs) {
+            Map<String, String> adviceDocStructureMap = adviceDoc.getStructureMap();
+            String name = adviceDocStructureMap.get("医嘱项目名称");
+            String type = adviceDocStructureMap.get("医嘱类型判别");
+            if (StringUtil.isNotBlank(type) && type.equals("出院带药")) {
+                if (StringUtil.isNotBlank(name)) {
+                    drugs.add(name);
+                }
+            }
+        }
+
+        String infoStr = "";
+        List<String> notContainsDrugs = new ArrayList<>();
+        for (String drug:drugs) {
+            if (!dischargeOrder.contains(drug)){
+                notContainsDrugs.add(drug);
+            }
+        }
+
+        //没有包含的药品再次查看商品名或化学名在出院医嘱书写
+        for (String drug : notContainsDrugs) {
+            Set<String> splitDrugs = CatalogueUtil.getRegexWords(drug, "[((\\[][^\\[\\]()()]+[\\]))]");
+            boolean isFind = false;
+            for (String sd : splitDrugs) {
+                if (dischargeOrder.contains(sd)){
+                    isFind = true;
+                }
+            }
+            if (!isFind) {
+                infoStr = CatalogueUtil.concatInfo(infoStr, drug);
+            }
+        }
+        if (StringUtil.isNotBlank(infoStr)){
+            status.set("-1");
+            info.set(infoStr);
+        }
+    }
+
+}

+ 130 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02891.java

@@ -0,0 +1,130 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.AnesthesiaRelatedDoc;
+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.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 手术记录手术开始时间与手麻系统记录不一致
+ * @author: 胡敬
+ * @time: 2020/5/12 10:23
+ */
+@Component
+public class OPE02891 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        //        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+        //        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        List<AnesthesiaRelatedDoc> anesthesiaRelatedDocs = inputInfo.getAnesthesiaRelatedDocs();
+        if (operationDocs == null || operationDocs.size() == 0
+                || anesthesiaRelatedDocs == null || anesthesiaRelatedDocs.size() == 0) {
+            return;
+        }
+
+        for (AnesthesiaRelatedDoc anesthesiaRelatedDoc : anesthesiaRelatedDocs) {
+            Map<String, String> anesthesiaStructureMap = anesthesiaRelatedDoc.getStructureMap();
+            String anesOperationName = anesthesiaStructureMap.get("手术名称");
+            String anesOperationStartDateStr = anesthesiaStructureMap.get("手术日期");
+            if (StringUtil.isBlank(anesOperationName)) {
+                continue;
+            }
+            anesOperationName = StringUtil.removeBlank(anesOperationName);
+            if (StringUtil.isBlank(anesOperationStartDateStr)) {
+                continue;
+            }
+            Date anesOperationStartDate = StringUtil.parseDateTime(anesOperationStartDateStr);
+            if (anesOperationStartDate == null) {
+                continue;
+            }
+            Map<String, Date> startEndDate = getStartEndDate(operationDocs, anesOperationName);
+            if (startEndDate.get("手术日期") != null) {
+                Date operationStartDate = startEndDate.get("手术日期");
+                if (!operationStartDate.equals(anesOperationStartDate)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+    //    }
+
+    /**
+     * 获取手术开始时间和结束时间
+     *
+     * @param operationDocs
+     * @param anesOperationName
+     * @return
+     */
+    private Map<String, Date> getStartEndDate(List<OperationDoc> operationDocs, String anesOperationName) {
+        Map<String, Date> startEndDateMap = new HashMap<>();
+        Date startDate = null, endDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> operationStructureMap = operationRecordDoc.getStructureMap();
+            String operationName = operationStructureMap.get("手术名称");
+            String operationStartDateStr = operationStructureMap.get("手术日期");
+            String operationEndDateStr = operationStructureMap.get("手术日期");
+            if (StringUtil.isBlank(operationName)) {
+                continue;
+            }
+            operationName = StringUtil.removeBlank(operationName);
+            if (withInOneDay(operationStartDateStr, operationEndDateStr)) {
+                if (anesOperationName.contains(operationName)) {
+                    Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                    if (startDate == null || operationStartDate.before(startDate)) {
+                        startDate = operationStartDate;
+                    }
+                    Date operationEndDate = StringUtil.parseDateTime(operationEndDateStr);
+                    if (endDate == null || operationEndDate.after(endDate)) {
+                        endDate = operationEndDate;
+                    }
+                }
+            }
+        }
+        if (startDate != null) {
+            startEndDateMap.put("手术日期", startDate);
+        }
+        if (endDate != null) {
+            startEndDateMap.put("手术日期", endDate);
+        }
+        return startEndDateMap;
+    }
+
+    /**
+     * 确认手术开始时间->手术结束时间是否为24小时内
+     *
+     * @param firstDateStr
+     * @param secondDateStr
+     * @return
+     */
+    private boolean withInOneDay(String firstDateStr, String secondDateStr) {
+        if (StringUtil.isBlank(firstDateStr) && StringUtil.isBlank(secondDateStr)) {
+            return false;
+        }
+        Date firstDate = StringUtil.parseDateTime(firstDateStr);
+        Date secondDate = StringUtil.parseDateTime(secondDateStr);
+        if (firstDate == null || secondDate == null) {
+            return false;
+        }
+        return firstDate.before(secondDate) && !CatalogueUtil.compareTime(firstDate, secondDate, 24 * 60L);
+    }
+
+}

+ 130 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02892.java

@@ -0,0 +1,130 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.AnesthesiaRelatedDoc;
+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.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 手术记录手术结束时间与手麻系统记录不一致
+ * @author: 胡敬
+ * @time: 2020/5/11 16:32
+ */
+@Component
+public class OPE02892 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        //        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+        //        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        List<AnesthesiaRelatedDoc> anesthesiaRelatedDocs = inputInfo.getAnesthesiaRelatedDocs();
+        if (operationDocs == null || operationDocs.size() == 0
+                || anesthesiaRelatedDocs == null || anesthesiaRelatedDocs.size() == 0) {
+            return;
+        }
+
+        for (AnesthesiaRelatedDoc anesthesiaRelatedDoc : anesthesiaRelatedDocs) {
+            Map<String, String> anesthesiaStructureMap = anesthesiaRelatedDoc.getStructureMap();
+            String anesOperationName = anesthesiaStructureMap.get("手术名称");
+            String anesOperationEndDateStr = anesthesiaStructureMap.get("手术日期");
+            if (StringUtil.isBlank(anesOperationName)) {
+                continue;
+            }
+            anesOperationName = StringUtil.removeBlank(anesOperationName);
+            if (StringUtil.isBlank(anesOperationEndDateStr)) {
+                continue;
+            }
+            Date anesOperationEndDate = StringUtil.parseDateTime(anesOperationEndDateStr);
+            if (anesOperationEndDate == null) {
+                continue;
+            }
+            Map<String, Date> startEndDate = getStartEndDate(operationDocs, anesOperationName);
+            if (startEndDate.get("手术日期") != null) {
+                Date operationEndDate = startEndDate.get("手术日期");
+                if (!operationEndDate.equals(anesOperationEndDate)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+    //    }
+
+    /**
+     * 获取手术开始时间和结束时间
+     *
+     * @param operationDocs
+     * @param anesOperationName
+     * @return
+     */
+    private Map<String, Date> getStartEndDate(List<OperationDoc> operationDocs, String anesOperationName) {
+        Map<String, Date> startEndDateMap = new HashMap<>();
+        Date startDate = null, endDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> operationStructureMap = operationRecordDoc.getStructureMap();
+            String operationName = operationStructureMap.get("手术名称");
+            String operationStartDateStr = operationStructureMap.get("手术日期");
+            String operationEndDateStr = operationStructureMap.get("手术日期");
+            if (StringUtil.isBlank(operationName)) {
+                continue;
+            }
+            operationName = StringUtil.removeBlank(operationName);
+            if (withInOneDay(operationStartDateStr, operationEndDateStr)) {
+                if (anesOperationName.contains(operationName)) {
+                    Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                    if (startDate == null || operationStartDate.before(startDate)) {
+                        startDate = operationStartDate;
+                    }
+                    Date operationEndDate = StringUtil.parseDateTime(operationEndDateStr);
+                    if (endDate == null || operationEndDate.after(endDate)) {
+                        endDate = operationEndDate;
+                    }
+                }
+            }
+        }
+        if (startDate != null) {
+            startEndDateMap.put("手术日期", startDate);
+        }
+        if (endDate != null) {
+            startEndDateMap.put("手术日期", endDate);
+        }
+        return startEndDateMap;
+    }
+
+    /**
+     * 确认手术开始时间->手术结束时间是否为24小时内
+     *
+     * @param firstDateStr
+     * @param secondDateStr
+     * @return
+     */
+    private boolean withInOneDay(String firstDateStr, String secondDateStr) {
+        if (StringUtil.isBlank(firstDateStr) && StringUtil.isBlank(secondDateStr)) {
+            return false;
+        }
+        Date firstDate = StringUtil.parseDateTime(firstDateStr);
+        Date secondDate = StringUtil.parseDateTime(secondDateStr);
+        if (firstDate == null || secondDate == null) {
+            return false;
+        }
+        return firstDate.before(secondDate) && !CatalogueUtil.compareTime(firstDate, secondDate, 24 * 60L);
+    }
+
+}

+ 130 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02897.java

@@ -0,0 +1,130 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.AnesthesiaRelatedDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDiscussionDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 术后首次病程手术结束时间与手麻系统记录不一致
+ * @author: 胡敬
+ * @time: 2020/5/12 10:23
+ */
+@Component
+public class OPE02897 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        //        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+        //        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        List<AnesthesiaRelatedDoc> anesthesiaRelatedDocs = inputInfo.getAnesthesiaRelatedDocs();
+        if (operationDocs == null || operationDocs.size() == 0
+                || anesthesiaRelatedDocs == null || anesthesiaRelatedDocs.size() == 0) {
+            return;
+        }
+
+        for (AnesthesiaRelatedDoc anesthesiaRelatedDoc : anesthesiaRelatedDocs) {
+            Map<String, String> anesthesiaStructureMap = anesthesiaRelatedDoc.getStructureMap();
+            String anesOperationName = anesthesiaStructureMap.get("手术名称");
+            String anesOperationEndDateStr = anesthesiaStructureMap.get("手术结束时间");
+            if (StringUtil.isBlank(anesOperationName)) {
+                continue;
+            }
+            anesOperationName = StringUtil.removeBlank(anesOperationName);
+            if (StringUtil.isBlank(anesOperationEndDateStr)) {
+                continue;
+            }
+            Date anesOperationEndDate = StringUtil.parseDateTime(anesOperationEndDateStr);
+            if (anesOperationEndDate == null) {
+                continue;
+            }
+            Map<String, Date> startEndDate = getStartEndDate(operationDocs, anesOperationName);
+            if (startEndDate.get("手术结束时间") != null) {
+                Date operationEndDate = startEndDate.get("手术结束时间");
+                if (!operationEndDate.equals(anesOperationEndDate)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+    //    }
+
+    /**
+     * 获取手术开始时间和结束时间
+     *
+     * @param operationDocs
+     * @param anesOperationName
+     * @return
+     */
+    private Map<String, Date> getStartEndDate(List<OperationDoc> operationDocs, String anesOperationName) {
+        Map<String, Date> startEndDateMap = new HashMap<>();
+        Date startDate = null, endDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationDiscussionDoc operationDiscussionDoc = operationDoc.getOperationDiscussionDoc();
+            if (operationDiscussionDoc == null) {
+                continue;
+            }
+            Map<String, String> operationStructureMap = operationDiscussionDoc.getStructureMap();
+            String operationName = operationStructureMap.get("手术名称");
+            String operationStartDateStr = operationStructureMap.get("手术日期");
+            String operationEndDateStr = operationStructureMap.get("手术日期");
+            if (StringUtil.isBlank(operationName)) {
+                continue;
+            }
+            operationName = StringUtil.removeBlank(operationName);
+            if (withInOneDay(operationStartDateStr, operationEndDateStr)) {
+                if (anesOperationName.contains(operationName)) {
+                    Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                    if (startDate == null || operationStartDate.before(startDate)) {
+                        startDate = operationStartDate;
+                    }
+                    Date operationEndDate = StringUtil.parseDateTime(operationEndDateStr);
+                    if (endDate == null || operationEndDate.after(endDate)) {
+                        endDate = operationEndDate;
+                    }
+                }
+            }
+        }
+        if (startDate != null) {
+            startEndDateMap.put("手术日期", startDate);
+        }
+        if (endDate != null) {
+            startEndDateMap.put("手术日期", endDate);
+        }
+        return startEndDateMap;
+    }
+
+    /**
+     * 确认手术开始时间->手术结束时间是否为24小时内
+     *
+     * @param firstDateStr
+     * @param secondDateStr
+     * @return
+     */
+    private boolean withInOneDay(String firstDateStr, String secondDateStr) {
+        if (StringUtil.isBlank(firstDateStr) && StringUtil.isBlank(secondDateStr)) {
+            return false;
+        }
+        Date firstDate = StringUtil.parseDateTime(firstDateStr);
+        Date secondDate = StringUtil.parseDateTime(secondDateStr);
+        if (firstDate == null || secondDate == null) {
+            return false;
+        }
+        return firstDate.before(secondDate) && !CatalogueUtil.compareTime(firstDate, secondDate, 24 * 60L);
+    }
+
+}

+ 130 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02898.java

@@ -0,0 +1,130 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.AnesthesiaRelatedDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDiscussionDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 术后首次病程手术开始时间与手麻系统记录不一致
+ * @author: 胡敬
+ * @time: 2020/5/12 10:23
+ */
+@Component
+public class OPE02898 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        //        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+        //        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        List<AnesthesiaRelatedDoc> anesthesiaRelatedDocs = inputInfo.getAnesthesiaRelatedDocs();
+        if (operationDocs == null || operationDocs.size() == 0
+                || anesthesiaRelatedDocs == null || anesthesiaRelatedDocs.size() == 0) {
+            return;
+        }
+
+        for (AnesthesiaRelatedDoc anesthesiaRelatedDoc : anesthesiaRelatedDocs) {
+            Map<String, String> anesthesiaStructureMap = anesthesiaRelatedDoc.getStructureMap();
+            String anesOperationName = anesthesiaStructureMap.get("手术名称");
+            String anesOperationStartDateStr = anesthesiaStructureMap.get("手术日期");
+            if (StringUtil.isBlank(anesOperationName)) {
+                continue;
+            }
+            anesOperationName = StringUtil.removeBlank(anesOperationName);
+            if (StringUtil.isBlank(anesOperationStartDateStr)) {
+                continue;
+            }
+            Date anesOperationStartDate = StringUtil.parseDateTime(anesOperationStartDateStr);
+            if (anesOperationStartDate == null) {
+                continue;
+            }
+            Map<String, Date> startEndDate = getStartEndDate(operationDocs, anesOperationName);
+            if (startEndDate.get("手术日期") != null) {
+                Date operationStartDate = startEndDate.get("手术日期");
+                if (!operationStartDate.equals(anesOperationStartDate)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+    //    }
+
+    /**
+     * 获取手术开始时间和结束时间
+     *
+     * @param operationDocs
+     * @param anesOperationName
+     * @return
+     */
+    private Map<String, Date> getStartEndDate(List<OperationDoc> operationDocs, String anesOperationName) {
+        Map<String, Date> startEndDateMap = new HashMap<>();
+        Date startDate = null, endDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationDiscussionDoc operationDiscussionDoc = operationDoc.getOperationDiscussionDoc();
+            if (operationDiscussionDoc == null) {
+                continue;
+            }
+            Map<String, String> operationStructureMap = operationDiscussionDoc.getStructureMap();
+            String operationName = operationStructureMap.get("手术名称");
+            String operationStartDateStr = operationStructureMap.get("手术日期");
+            String operationEndDateStr = operationStructureMap.get("手术日期");
+            if (StringUtil.isBlank(operationName)) {
+                continue;
+            }
+            operationName = StringUtil.removeBlank(operationName);
+            if (withInOneDay(operationStartDateStr, operationEndDateStr)) {
+                if (anesOperationName.contains(operationName)) {
+                    Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                    if (startDate == null || operationStartDate.before(startDate)) {
+                        startDate = operationStartDate;
+                    }
+                    Date operationEndDate = StringUtil.parseDateTime(operationEndDateStr);
+                    if (endDate == null || operationEndDate.after(endDate)) {
+                        endDate = operationEndDate;
+                    }
+                }
+            }
+        }
+        if (startDate != null) {
+            startEndDateMap.put("手术日期", startDate);
+        }
+        if (endDate != null) {
+            startEndDateMap.put("手术日期", endDate);
+        }
+        return startEndDateMap;
+    }
+
+    /**
+     * 确认手术开始时间->手术结束时间是否为24小时内
+     *
+     * @param firstDateStr
+     * @param secondDateStr
+     * @return
+     */
+    private boolean withInOneDay(String firstDateStr, String secondDateStr) {
+        if (StringUtil.isBlank(firstDateStr) && StringUtil.isBlank(secondDateStr)) {
+            return false;
+        }
+        Date firstDate = StringUtil.parseDateTime(firstDateStr);
+        Date secondDate = StringUtil.parseDateTime(secondDateStr);
+        if (firstDate == null || secondDate == null) {
+            return false;
+        }
+        return firstDate.before(secondDate) && !CatalogueUtil.compareTime(firstDate, secondDate, 24 * 60L);
+    }
+
+}

+ 91 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE02930.java

@@ -0,0 +1,91 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.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.time.DateUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : OPE02930
+ * @Description : 术后首次病程未即刻完成
+ * @Author : 胡敬
+ * @Date: 2020-06-15 17:30
+ */
+@Component
+public class OPE02930 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+        //        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> operationRecordStructureMap = operationRecordDoc.getStructureMap();
+            String operationStartDateStr = operationRecordStructureMap.get("手术日期");
+            String operationEndDateStr = operationRecordStructureMap.get("手术日期");
+            if (StringUtil.isEmpty(operationStartDateStr) || StringUtil.isEmpty(operationEndDateStr)) {
+                continue;
+            }
+            if (!isDate(operationStartDateStr) || !isDate(operationEndDateStr)) {
+                continue;
+            }
+            Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+            Date operationEndDate = StringUtil.parseDateTime(operationEndDateStr);
+            for (OperationDoc o : operationDocs) {
+                OperationDiscussionDoc operationDiscussionDoc = o.getOperationDiscussionDoc();
+                if (operationDiscussionDoc == null) {
+                    continue;
+                }
+                Map<String, String> structureMap = operationDiscussionDoc.getStructureMap();
+                String discussionRecordDateStr = structureMap.get("病历日期");
+                if (StringUtil.isBlank(discussionRecordDateStr)) {
+                    status.set("0");
+                    return;
+                }
+
+                if (!isDate(discussionRecordDateStr)) {
+                    status.set("0");
+                    return;
+                }
+                Date discussionRecordDate = StringUtil.parseDateTime(discussionRecordDateStr);
+                if (operationEndDate.before(discussionRecordDate) || DateUtils.isSameDay(operationEndDate, discussionRecordDate)) {
+                    if (CatalogueUtil.compareTime(operationEndDate, discussionRecordDate, 2 * 60L)) {
+                        status.set("-1");
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 判断是否为日期
+     *
+     * @param dateStr
+     */
+    private boolean isDate(String dateStr) {
+        try {
+            StringUtil.parseDateTime(dateStr);
+            return true;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+}

+ 177 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE0369.java

@@ -0,0 +1,177 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.MedicalRecordInfoDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.*;
+
+/**
+ * @Description: 手术患者缺术前主刀医师查房记录
+ * @author: WANGSY
+ * @time: 2020/11/11 11:22
+ */
+@Component
+public class OPE0369 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        if (outputInfo.getResult() != null) {
+            Map<String, Object> thr03090 = outputInfo.getResult().get("THR03090");
+            if (thr03090 != null && thr03090.size() > 0 && thr03090.get("status").equals("-1")) {
+                return;
+            }
+        }
+
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        String admisTime = "";
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime)) {
+                return;
+            }
+        }
+        //先判断是否有手术记录
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            return;
+        }
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        int operationCount = getOperationSum(operationDocs); // 获取手术记录次数
+//        long operationCount = operationDocs.stream().filter(operationDoc -> operationDoc.getOperationRecordDoc() != null).count();
+        //主刀查房次数
+        long operateCount = 0;
+        if (operationCount > 0) {
+            //存在手术记录无查房记录
+            if (ListUtil.isEmpty(threeLevelWardDocs)) {
+                status.set("-1");
+                return;
+            }
+            String operationStartDate = "";
+            List<Date> operDateList = new ArrayList<>();
+            for (OperationDoc operationDoc : operationDocs) {
+                if (operationDoc.getOperationRecordDoc() != null) {
+                    Map<String, String> operationDocStructureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+                    operationStartDate = operationDocStructureMap.get("手术日期");
+                    if (operationStartDate.contains("年月日")) {
+                        continue;
+                    }
+                    if (StringUtil.isNotBlank(operationStartDate)) {
+                        if (!CatalogueUtil.compareTime(
+                                StringUtil.parseDateTime(admisTime),
+                                StringUtil.parseDateTime(operationStartDate),
+                                Long.valueOf(30))) {
+                            continue;
+                        }
+                        operDateList.add(StringUtil.parseDateTime(operationStartDate));
+                    } else {//取不到手术时间
+                        return;
+                    }
+                }
+            }
+
+            if (operDateList.size() > 1) {
+                for (int i = 0; i < operDateList.size(); i++) {
+                    if (i + 1 < operDateList.size()) {
+                        if (!CatalogueUtil.compareTime(operDateList.get(i), operDateList.get(i + 1),
+                                Long.valueOf(24 * 60))) {//如果手术记录是同一天,需有一次术前主刀查房
+                            operationCount--;
+                        }
+                    }
+                }
+            }
+
+            ThreeLevelWardDoc threeLevelWardDoc = threeLevelWardDocs.get(0);
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+                Map<String, String> structureMap = threeLevelWard.getStructureMap();
+                String makeTitle = structureMap.get("查房标题");
+                String writTitle = structureMap.get("文书标题");
+                String recordDateStr = structureMap.get("查房日期");
+                if (operDateList.size() > 0) {
+                    for (Date date : operDateList) {
+                        if (StringUtil.isNotBlank(recordDateStr) && date != null
+                                && StringUtil.parseDateTime(recordDateStr).before(date)) {
+                            if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(recordDateStr), date, Long.valueOf(24 * 60))
+                                    && (
+                                    ((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("主刀")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("主刀")))
+                                    ||((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("主任")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("主任")))
+                                    ||((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("三级")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("三级")))
+                                    || ((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("术前"))|| (StringUtil.isNotBlank(writTitle) && writTitle.contains("术前")))
+                            )) {
+                                operateCount++;
+                            }
+                        }
+                    }
+                } else {//手术开始时间跟入院时间 小于30分钟,规则不判断
+                    return;
+                }
+            }
+        }
+
+        if (operationCount > 0 && operationCount > operateCount) {
+            status.set("-1");
+            return;
+        }
+    }
+
+
+    /**
+     * 获取手术记录次数
+     *
+     * @param operationDocs
+     * @return
+     */
+    private int getOperationSum(List<OperationDoc> operationDocs) {
+        List<Map<String, Date>> operationDateList = new ArrayList<>();
+        Map<String, Date> operationDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> structureMap = operationRecordDoc.getStructureMap();
+            String operationStartDateStr = structureMap.get("手术日期");
+            if (StringUtil.isNotBlank(operationStartDateStr) && !operationStartDateStr.contains("年月日")) {
+                Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                operationStartDate = DateUtil.dateZeroClear(operationStartDate);
+                if (operationStartDate != null) {
+                    /* 放第一个手术记录的日期到operationDateList */
+                    if (operationDateList.size() == 0) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术日期", operationStartDate);
+                        operationDateList.add(operationDate);
+                        continue;
+                    }
+                    /* 如果其中一个手术记录的开始时间到结束时间之间还包含另一个手术,就不往operationDateList里加 */
+                    boolean findInnerOperation = false;
+                    for (Map<String, Date> date : operationDateList) {
+                        Date listStartDate = DateUtil.dateZeroClear(date.get("手术日期"));
+                        if (listStartDate.equals(operationStartDate)) {
+                            findInnerOperation = true;
+                            break;
+                        }
+                    }
+                    if (!findInnerOperation) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术日期", operationStartDate);
+                        operationDateList.add(operationDate);
+                    }
+                }
+            }
+        }
+        return operationDateList.size();
+    }
+}

+ 48 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE0628.java

@@ -0,0 +1,48 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+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.operation.OperationDoc;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.List;
+
+/**
+ * @Description: 缺手术开始时间
+ * @author: 胡敬
+ * @time: 2020/3/28 16:32
+ */
+@Component
+public class OPE0628 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+//        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+//        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if(operationDocs == null || operationDocs.size() == 0){
+            status.set("0");
+            return;
+        }
+        if (ListUtil.isNotEmpty(operationDocs)) {
+            long count = operationDocs.stream().filter(operationDoc -> {
+                boolean flag = false;
+                if (operationDoc.getOperationRecordDoc() != null
+                        && StringUtil.isBlank(operationDoc.getOperationRecordDoc().getStructureMap().get("手术日期"))) {
+                    flag = true;
+                }
+                return flag;
+            }).count();
+            if (count > 0) {
+                status.set("-1");
+            }
+        }
+    }
+//    }
+
+}

+ 54 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/operationdiscussion/OPE0630.java

@@ -0,0 +1,54 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.operationdiscussion;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @Description: 手术开始时间不规范
+ * @author: 胡敬
+ * @time: 2020/3/28 16:32
+ */
+@Component
+public class OPE0630 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        //        boolean isOperativePatient = true;//是手术患者(暂时默认是)
+        //        if (isOperativePatient) {
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
+        if (ListUtil.isNotEmpty(operationDocs)) {
+            for (OperationDoc operationDoc : operationDocs) {
+                if (operationDoc.getOperationRecordDoc() == null){
+                    continue;
+                }
+                String operationStartDateStr = operationDoc.getOperationRecordDoc().getStructureMap().get("手术日期");
+                if (CatalogueUtil.isEmpty(operationStartDateStr)) {
+                    continue;
+                }
+                Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr, CatalogueUtil.processDateFormat(Content.dateFormats));
+                if (null == operationStartDate) {
+                    status.set("-1");
+                    return;
+                }
+            }
+            //            }
+        }
+    }
+
+}

+ 123 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/preoperativediscussion/PRE03064.java

@@ -0,0 +1,123 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.preoperativediscussion;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+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.LisDoc;
+import com.lantone.qc.pub.model.doc.PacsDoc;
+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.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @Description: 术前未完成常规检查
+ * @author: HUJING
+ * @time: 2020/8/14 13:14
+ */
+@Component
+public class PRE03064 extends QCCatalogue {
+    private List<String> normalCheck = Lists.newArrayList("出凝血时间", "HBSAG", "血常规", "尿常规", "血型", "心电图");
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        status.set("0");
+        List<LisDoc> lisDocs = inputInfo.getLisDocs();
+        List<PacsDoc> pacsDocs = inputInfo.getPacsDocs();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+
+        if (operationDocs.size() == 0 || lisDocs.size() == 0 || pacsDocs.size() == 0) {
+            return;
+        }
+
+        //取化验名称
+        Map<String, String> lisRet = Maps.newHashMap();
+        lisDocs.stream().map(LisDoc::getStructureMap).forEach(x -> lisRet.put(x.get("报告名称"), x.get("报告创建时间")));
+        Map<String, String> newLisRet = processMap(lisRet);
+        //取辅检名称
+        Map<String, String> pacsRet = Maps.newHashMap();
+        pacsDocs.stream().map(PacsDoc::getStructureMap).forEach(x -> pacsRet.put(x.get("报告名称"), x.get("报告创建时间")));
+        Map<String, String> newPacsRet = processMap(pacsRet);
+
+        //取手术时间
+        List<String> operationDateList = operationDocs.stream().map(OperationDoc::getOperationRecordDoc)
+                .filter(Objects::nonNull)
+                .map(OperationRecordDoc::getStructureMap).map(x -> x.get("手术日期")).filter(StringUtil::isNotBlank).collect(Collectors.toList());
+
+        List<String> lisKey = new ArrayList<>(newLisRet.keySet());
+        List<String> pacsKey = new ArrayList<>(newPacsRet.keySet());
+        //lisKey.retainAll(normalCheck);//如果存在相同元素,lisKey中仅保留相同的元素。 如果不存在相同元素,lisKey会变为空。
+        //pacsKey.retainAll(normalCheck);//如果存在相同元素,pacsKey中仅保留相同的元素。 如果不存在相同元素,pacsKey会变为空。
+
+        if (operationDateList.size() > 0) {
+            String firstOperationDateStr = operationDateList.get(0);
+            Date firstOperationDate = StringUtil.parseDateTime(firstOperationDateStr);
+            int matchSum = 0;
+            List<String> missCheck = Lists.newArrayList();
+            getMatchSum(newLisRet, lisKey, firstOperationDate, matchSum, missCheck);
+            getMatchSum(newPacsRet, pacsKey, firstOperationDate, matchSum, missCheck);
+
+            if (matchSum < normalCheck.size()) {
+                status.set("-1");
+                info.set("未完成常规检查:" + missCheck.toString().replaceAll("[\\[\\]]", ""));
+            }
+        }
+//        for (String operationDateStr : operationDateList) {
+//            Date operationDate = StringUtil.parseDateTime(operationDateStr);
+//            if (operationDate == null) {
+//                continue;
+//            }
+//
+//        }
+//        System.out.println(operationDateList);
+
+    }
+
+    //获取检查中与必须要的检查匹配数
+    private void getMatchSum(Map<String, String> newRet, List<String> keySet, Date firstOperationDate, int matchSum, List<String> miss) {
+        for (String check : normalCheck) {
+            int checkMiss = 0;
+            for (String lis : keySet) {
+                if (lis.contains(check)) {
+                    String lisDateStr = newRet.get(lis);
+                    Date lisDate = StringUtil.parseDateTime(lisDateStr);
+                    if (lisDate.before(firstOperationDate)) {
+                        matchSum++;
+                        break;
+                    } else {
+                        checkMiss++;
+                    }
+                } else {
+                    checkMiss++;
+                }
+
+            }
+            //如果所有化验里都没有必要的当前检查(normalCheck里的检查)
+            if (checkMiss == keySet.size()) {
+                miss.add(check);
+            }
+        }
+    }
+
+    private Map<String, String> processMap(Map<String, String> ret) {
+        Map<String, String> newRet = Maps.newHashMap();
+        for (Map.Entry<String, String> enrty : ret.entrySet()) {
+            String key = enrty.getKey();
+            if (key.contains("=")) {
+                String pubName = key.split("=")[0];
+                if (StringUtil.isNotBlank(pubName)) {
+                    newRet.put(pubName, enrty.getValue());
+                }
+            } else {
+                newRet.put(key, enrty.getValue());
+            }
+        }
+        return newRet;
+    }
+}

+ 136 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/preoperativediscussion/PRE0328.java

@@ -0,0 +1,136 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.preoperativediscussion;
+
+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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.text.ParseException;
+import java.util.*;
+
+/**
+ * @Description: 手术患者无术前讨论记录
+ * @author: rengb
+ * @time: 2020/3/23 15:09
+ */
+@Component
+public class PRE0328 extends QCCatalogue {
+
+    @Override
+    protected void start(InputInfo inputInfo, OutputInfo outputInfo) throws ParseException {
+        /**
+         * 1:如果急诊手术【入院时间-手术开始时间小于30分钟】则可不用写术前小结。
+         * 2:如果入院时间-手术开始时间小于30分钟,则术前讨论、术前小结次数+1。
+         * 3:如果手术记录次数(第一次手术的日期内有其他手术不算次数) 大于 术前讨论、术前小结次数,则出错
+         */
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            return;
+        }
+        boolean emergencyOperation = false;
+        if (inputInfo.getBeHospitalizedDoc() != null) {
+            Map<String, String> beHospitalStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+            String admisTime = beHospitalStructureMap.get("入院日期");
+            //取手术记录第一次手术开始时间和入院时间比较,相差30分钟内不报缺陷
+            OperationRecordDoc operationRecordDoc = operationDocs.get(0).getOperationRecordDoc();
+            String startTime = "";
+            if (operationRecordDoc != null) {
+                startTime = operationRecordDoc.getStructureMap().get("手术日期");
+            }
+
+            if (StringUtil.isNotBlank(startTime) && StringUtil.isNotBlank(admisTime) &&
+                    !startTime.contains("年月日")) {
+                if (!CatalogueUtil.compareTime(
+                        StringUtil.parseDateTime(admisTime),
+                        StringUtil.parseDateTime(startTime),
+                        Long.valueOf(30))) {//入院到手术未超过30分钟,则可不用写术前小结
+                    emergencyOperation = true;
+                }
+            }
+        }
+
+        int i = getOperationSum(operationDocs); // 获取手术记录次数
+        int j = 0;  // 获取术前讨论、术前小结次数
+        for (OperationDoc operationDoc : operationDocs) {
+            if (operationDoc.getPreoperativeDiscussionDoc() != null) {
+                j++;
+            }
+        }
+        /* 如果入院时间-手术开始时间小于30分钟,则术前讨论、术前小结次数+1*/
+        if (emergencyOperation) {
+            if (j == 0) {
+                j++;
+            }
+        }
+        //医嘱中包含“冠状动脉造影术”,且无术前讨论.则报规则
+        for (DoctorAdviceDoc doctorAdviceDoc : doctorAdviceDocs) {
+            Map<String, String> doctorAdviceStructuerMap = doctorAdviceDoc.getStructureMap();
+            String advicename = doctorAdviceStructuerMap.get("医嘱项目名称");
+            if (StringUtil.isNotBlank(advicename) && advicename.contains("冠状动脉造影术") && j == 0) {
+                status.set("-1");
+                info.set("手术记录不一致");
+            }
+        }
+
+        if (i > 0 && i > j) {
+            status.set("-1");
+            info.set("手术记录不一致");
+        }
+    }
+
+    /**
+     * 获取手术记录次数
+     *
+     * @param operationDocs
+     * @return
+     */
+    private int getOperationSum(List<OperationDoc> operationDocs) {
+        List<Map<String, Date>> operationDateList = new ArrayList<>();
+        Map<String, Date> operationDate = null;
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> structureMap = operationRecordDoc.getStructureMap();
+            String operationStartDateStr = structureMap.get("手术日期");
+            if (StringUtil.isNotBlank(operationStartDateStr) && !operationStartDateStr.contains("年月日")) {
+                Date operationStartDate = StringUtil.parseDateTime(operationStartDateStr);
+                operationStartDate = DateUtil.dateZeroClear(operationStartDate);
+                if (operationStartDate != null) {
+                    /* 放第一个手术记录的日期到operationDateList */
+                    if (operationDateList.size() == 0) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术日期", operationStartDate);
+                        operationDateList.add(operationDate);
+                        continue;
+                    }
+                    /* 如果其中一个手术记录的开始时间到结束时间之间还包含另一个手术,就不往operationDateList里加 */
+                    boolean findInnerOperation = false;
+                    for (Map<String, Date> date : operationDateList) {
+                        Date listStartDate = DateUtil.dateZeroClear(date.get("手术日期"));
+                        if (listStartDate.equals(operationStartDate)) {
+                            findInnerOperation = true;
+                            break;
+                        }
+                    }
+                    if (!findInnerOperation) {
+                        operationDate = new HashMap<>();
+                        operationDate.put("手术日期", operationStartDate);
+                        operationDateList.add(operationDate);
+                    }
+                }
+            }
+        }
+        return operationDateList.size();
+    }
+
+}

+ 188 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0125.java

@@ -0,0 +1,188 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+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.MedicalRecordInfoDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.ward.DirectorDoctorWardDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR0125
+ * @Description : 副主任医师/主任医师查房首次查房记录未在患者入院72h内完成
+ * @Author : 胡敬
+ * @Date: 2020-03-19 13:51
+ */
+@Component
+public class THR0125 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            String admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            //出院日期
+            String dischargeTime = medicalRecordInfoDoc.getStructureMap().get("leaveHospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime) || CatalogueUtil.isEmpty(dischargeTime)) {
+                status.set("0");
+                return;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(admisTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(72 * 60))) {//如果入院未超过72小时,规则不判断
+                status.set("0");
+                return;
+            }
+            //如果住院天数小于3天则不判断该条规则
+            if (DateUtil.parseDate(dischargeTime) != null &&
+                    !CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(dischargeTime), (long) (72 * 60))) {
+                status.set("0");
+                return;
+            } else {
+                if (inputInfo.getThreeLevelWardDocs().size() == 0) {
+                    return;
+                }
+            }
+//        }
+//
+//        if (inputInfo.getBeHospitalizedDoc() != null) {
+//            Map<String, String> beHospitalStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+//            String admisTime = beHospitalStructureMap.get("入院日期");
+            if (CatalogueUtil.isEmpty(admisTime)) {
+                status.set("0");
+                return;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(admisTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(72 * 60))) {//如果入院未超过72小时,规则不判断
+                status.set("0");
+                return;
+            }
+            //开始时间(入院时间)
+            Date beginDate = StringUtil.parseDateTime(admisTime);
+            if (beginDate == null) {
+                status.set("0");
+                return;
+            }
+            ThreeLevelWardDoc threeLevelWardDoc = inputInfo.getThreeLevelWardDocs().get(0);
+            List<DirectorDoctorWardDoc> directorDoctorWardDocs = threeLevelWardDoc.getDirectorDoctorWardDocs();
+            if (directorDoctorWardDocs.size() > 0) {
+                DirectorDoctorWardDoc firstDirector = directorDoctorWardDocs.get(0);
+                String wardDateStr = firstDirector.getStructureMap().get("查房日期");
+                if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(wardDateStr), 72 * 60L)) {
+                    status.set("0");
+                    return;
+                }
+            } else if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), new Date(), 72 * 60L)) {
+                status.set("0");//没有主任医师查房,若入院时间和系统当前时间对比,相差72小时则报错
+                return;
+            }
+
+            //===========三级医师相当于主治医生==========
+            List<ThreeLevelWardDoc>  allDoctorWradDocs =  threeLevelWardDoc.getAllDoctorWradDocs();
+            if(allDoctorWradDocs.size()>0)
+            {
+
+                for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+                    Map<String, String> structureMap = threeLevelWard.getStructureMap();
+                    String makeTitle = structureMap.get("查房标题");
+                    if(StringUtil.isNotBlank(makeTitle)&&makeTitle.contains("三级")||
+                            StringUtil.isNotBlank(makeTitle)&&makeTitle.contains("主任"))
+                    {
+                        String recordDateStr = structureMap.get("查房日期");
+                        if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime),
+                                StringUtil.parseDateTime(recordDateStr), 72 * 60L)) {
+                            status.set("0");
+                        }
+                        return;
+                    }
+                }
+
+
+            }
+
+            /* 如果存在手术记录,判断主刀医生是否为主治医生 */
+//            String operatorName = "";
+//            List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+//            if (operationDocs != null) {
+//                for (OperationDoc operationDoc : operationDocs) {
+//                    if (operationDoc.getOperationRecordDoc() != null) {
+//                        Map<String, String> operationDocStructureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+//                        if (StringUtil.isBlank(operatorName)) {
+//                            operatorName = operationDocStructureMap.get("主刀医师");
+//                            if (StringUtil.isBlank(operatorName) && StringUtil.isNotBlank(operationDocStructureMap.get("手术者及助手名称"))) {
+//                                operatorName = operationDocStructureMap.get("手术者及助手名称").split("、")[0];
+//                                if (operatorName.contains("主刀:") && operatorName.split(":").length > 1) {
+//                                    operatorName = operatorName.split(":")[1];
+//                                }
+//                            }
+//                        }
+//                    }
+//                }
+//
+//                List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
+//                for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+//                    Map<String, String> structureMap = threeLevelWard.getStructureMap();
+//                    String wardDateStr = structureMap.get("查房日期");
+//                    if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(wardDateStr), 72 * 60L)) {
+//                        String makeTitle = structureMap.get("查房标题");
+//                        String pathography = structureMap.get("病情记录");
+//                        //判断标题
+//                        visitingPhysicianHouse(operatorName, makeTitle);
+//                        //判断病情记录
+//                        visitingPhysicianHouse(operatorName, pathography);
+//                    }
+//                }
+//            }
+//        }
+        }
+    }
+
+//    /**
+//     * 判断查房记录是否含有主任医师查房
+//     *
+//     * @param operatorName     主刀医师
+//     * @param titlePathography 查房标题或病情记录
+//     */
+//    private void visitingPhysicianHouse(String operatorName, String titlePathography) {
+//        if (StringUtil.isNotBlank(titlePathography)) {
+//            if (titlePathography.contains("主任")) {
+//                status.set("0");
+//                return;
+//            } else if (StringUtil.isNotBlank(operatorName) && (titlePathography.contains(operatorName) || titlePathography.contains("主刀"))) {
+//                String operationProfessor = getCourseProfessor(operatorName);
+//                if (operationProfessor.contains("主任")) {
+//                    status.set("0");
+//                    return;
+//                }
+//            }
+//        }
+//    }
+
+//    private String getCourseProfessor(String operatorName) {
+//        String professor = "";
+//        if (StringUtil.isBlank(operatorName)) {
+//            return professor;
+//        }
+//        SpecialStorageUtil specialStorageUtil = SpringContextUtil.getBean("specialStorageUtil");
+//        Map<String, Object> surgeon = specialStorageUtil.getJsonStringValue(KernelConstants.HOSPITAL_DOCTOR_MAP);
+//        if (surgeon != null) {
+//            Map<String, String> doctor = (Map) surgeon.get(operatorName);
+//            if (doctor != null) {
+//                professor = doctor.get("professor");
+//            }
+//
+//        }
+//        return professor;
+//    }
+}

+ 222 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0126.java

@@ -0,0 +1,222 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.dbanaly.util.KernelConstants;
+import com.lantone.qc.dbanaly.util.SpecialStorageUtil;
+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.MedicalRecordInfoDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.ward.AttendingDoctorWardDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.SpringContextUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import com.lantone.qc.trans.comsis.CommonAnalysisUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR0126
+ * @Description : 主治医师首次查房未在48小时内完成
+ * @Author : 胡敬
+ * @Date: 2020-03-19 15:50
+ */
+@Component
+public class THR0126 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            String admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            //出院日期
+            String dischargeTime = medicalRecordInfoDoc.getStructureMap().get("leaveHospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime) || CatalogueUtil.isEmpty(dischargeTime)) {
+                status.set("0");
+                return;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(admisTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(48 * 60))) {//如果入院未超过48小时,规则不判断
+                status.set("0");
+                return;
+            }
+            //如果住院天数小于2天则不判断该条规则
+            if (DateUtil.parseDate(dischargeTime) != null && StringUtil.isNotEmpty(dischargeTime) &&
+                    !CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(dischargeTime), (long) (48 * 60))) {
+                status.set("0");
+                return;
+            } else {
+                if (inputInfo.getThreeLevelWardDocs().size() == 0) {
+                    return;
+                }
+            }
+//        }
+//
+//        if (inputInfo.getBeHospitalizedDoc() != null) {
+//            Map<String, String> beHospitalStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+//            String admisTime = beHospitalStructureMap.get("入院日期");
+            if (CatalogueUtil.isEmpty(admisTime)) {
+                status.set("0");
+                return;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(admisTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(48 * 60))) {//如果入院未超过48小时,规则不判断
+                status.set("0");
+                return;
+            }
+            //开始时间(入院时间)
+            Date beginDate = StringUtil.parseDateTime(admisTime);
+            if (beginDate == null) {
+                status.set("0");
+                return;
+            }
+            /* 首次病程录如果是主治或者主任写的,也算主治查房 */
+            FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+            if (firstCourseRecordDoc != null) {
+                String doctorSign = firstCourseRecordDoc.getStructureMap().get("医生签名");
+                String firstCourseProfessor = getFirstCourseProfessor(doctorSign);
+                if (StringUtil.isNotBlank(firstCourseProfessor) && firstCourseProfessor.contains("主治")) {
+                    String recordDateStr = firstCourseRecordDoc.getStructureMap().get("记录时间");
+                    if (StringUtil.isNotBlank(recordDateStr)) {
+                        if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(recordDateStr), 48 * 60L)) {
+                            status.set("0");
+                            return;
+                        }
+                    }
+                }
+                //妇幼首次病程录写法类似查房
+                if(StringUtil.isNotBlank(firstCourseRecordDoc.getText()))
+                {
+                    String text = firstCourseRecordDoc.getText();
+                    String data = CommonAnalysisUtil.NHExtractDate(text);
+                    if (StringUtil.isNotBlank(data)) {
+                        if (!CatalogueUtil.compareTime
+                                (StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(data), 48 * 60L)) {
+                            status.set("0");
+                            return;
+                        }
+                    }
+                }
+            }
+            ThreeLevelWardDoc threeLevelWardDoc = inputInfo.getThreeLevelWardDocs().get(0);
+            List<AttendingDoctorWardDoc> attendingDoctorWardDocs = threeLevelWardDoc.getAttendingDoctorWardDocs();
+            if (attendingDoctorWardDocs.size() > 0) {
+                AttendingDoctorWardDoc firstAttending = attendingDoctorWardDocs.get(0);
+                String wardDateStr = firstAttending.getStructureMap().get("查房日期");
+                if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(wardDateStr), 48 * 60L)) {
+                    status.set("0");
+                    return;
+                }
+            } else if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), new Date(), 48 * 60L)) {
+                status.set("0");//没有主治医师查房,若入院时间和系统当前时间对比,相差48小时则报错
+                return;
+            }
+            //===========三级医师相当于主治医生==========
+            List<ThreeLevelWardDoc>  allDoctorWradDocs =  threeLevelWardDoc.getAllDoctorWradDocs();
+            if(allDoctorWradDocs.size()>0)
+            {
+
+                for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+                    Map<String, String> structureMap = threeLevelWard.getStructureMap();
+                    String makeTitle = structureMap.get("查房标题");
+                    if(StringUtil.isNotBlank(makeTitle))
+                    {
+                        if(makeTitle.contains("主任")&&makeTitle.contains("三级"))
+                        {
+                            String recordDateStr = structureMap.get("查房日期");
+                            if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime),
+                                    StringUtil.parseDateTime(recordDateStr), 48 * 60L)) {
+                                status.set("0");
+                            }
+                            return;
+                        }
+                    }
+                }
+
+
+            }
+
+            /* 如果存在手术记录,判断主刀医生是否为主治医生 */
+//            String operatorName = "";
+//            List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+//            if (operationDocs != null) {
+//                for (OperationDoc operationDoc : operationDocs) {
+//                    if (operationDoc.getOperationRecordDoc() != null) {
+//                        Map<String, String> operationDocStructureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+//                        if (StringUtil.isBlank(operatorName)) {
+//                            operatorName = operationDocStructureMap.get("主刀医师");
+//                            if (StringUtil.isBlank(operatorName) && StringUtil.isNotBlank(operationDocStructureMap.get("手术者及助手名称"))) {
+//                                operatorName = operationDocStructureMap.get("手术者及助手名称").split("、")[0];
+//                                if (operatorName.contains("主刀:") && operatorName.split(":").length > 1) {
+//                                    operatorName = operatorName.split(":")[1];
+//                                }
+//                            }
+//                        }
+//                    }
+//                }
+//            }
+
+//            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
+//            for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+//                Map<String, String> structureMap = threeLevelWard.getStructureMap();
+//                String wardDateStr = structureMap.get("查房日期");
+//                if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(wardDateStr), 48 * 60L)) {
+//                    String makeTitle = structureMap.get("查房标题");
+//                    String pathography = structureMap.get("病情记录");
+//                    //判断标题
+//                    visitingPhysicianHouse(operatorName, makeTitle);
+//                    //判断病情记录
+//                    visitingPhysicianHouse(operatorName, pathography);
+//                }
+//            }
+        }
+    }
+
+//    /**
+//     * 判断查房记录是否含有主治医师查房
+//     *
+//     * @param operatorName     主刀医师
+//     * @param titlePathography 查房标题或病情记录
+//     */
+//    private void visitingPhysicianHouse(String operatorName, String titlePathography) {
+//        if (StringUtil.isNotBlank(titlePathography)) {
+//            if (titlePathography.contains("主治")) {
+//                status.set("0");
+//                return;
+//            } else if (StringUtil.isNotBlank(operatorName) && (titlePathography.contains(operatorName) || titlePathography.contains("主刀"))) {
+//                String operationProfessor = getFirstCourseProfessor(operatorName);
+//                if (operationProfessor.contains("主治")) {
+//                    status.set("0");
+//                    return;
+//                }
+//            }
+//        }
+//    }
+//
+    private String getFirstCourseProfessor(String doctorSign) {
+        String professor = "";
+//        String doctorSign = firstCourseRecordDoc.getStructureMap().get("医生签名");
+        if (StringUtil.isBlank(doctorSign)) {
+            return professor;
+        }
+        SpecialStorageUtil specialStorageUtil = SpringContextUtil.getBean("specialStorageUtil");
+        Map<String, Object> surgeon = specialStorageUtil.getJsonStringValue(KernelConstants.HOSPITAL_DOCTOR_MAP);
+        if (surgeon != null) {
+            Map<String, String> doctor = (Map) surgeon.get(doctorSign);
+            if (doctor != null) {
+                professor = doctor.get("professor");
+            }
+
+        }
+        return professor;
+    }
+}

+ 229 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0128.java

@@ -0,0 +1,229 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.dbanaly.util.KernelConstants;
+import com.lantone.qc.dbanaly.util.SpecialStorageUtil;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+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.MedicalRecordInfoDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.ward.AttendingDoctorWardDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+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.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR0128
+ * @Description : 每周无3次主治医师查房记录
+ * @Author : 胡敬
+ * @Date: 2020-03-19 16:52
+ */
+@Component
+public class THR0128 extends QCCatalogue {
+    @Autowired
+    private SpecialStorageUtil specialStorageUtil;
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            String admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            //出院日期
+            String dischargeTime = medicalRecordInfoDoc.getStructureMap().get("leaveHospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime)) {
+                return;
+            }
+            String presentTime = DateUtil.nowString();
+            //如果如果入院未超过7天则不判断该条规则
+            if (!CatalogueUtil.isEmpty(admisTime) && !CatalogueUtil.isEmpty(presentTime)) {
+                if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(presentTime), (long) (7 * 24 * 60))) {
+                    return;
+                }
+            }
+            //如果住院天数小于7天则不判断该条规则
+            if (!CatalogueUtil.isEmpty(admisTime) && !CatalogueUtil.isEmpty(dischargeTime)) {
+                if (!CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(dischargeTime), (long) (7 * 24 * 60))) {
+                    return;
+                }
+            } else {
+                if (inputInfo.getThreeLevelWardDocs().size() == 0) {
+                    status.set("-1");
+                    return;
+                }
+            }
+
+            if (inputInfo.getBeHospitalizedDoc() != null && inputInfo.getThreeLevelWardDocs().size() > 0) {
+                if (CatalogueUtil.isEmpty(admisTime)) {
+                    return;
+                }
+                //开始时间(入院时间)
+                Date beginDate = StringUtil.parseDateTime(admisTime);
+                if (beginDate == null) {
+                    return;
+                }
+
+                ThreeLevelWardDoc threeLevelWardDoc = inputInfo.getThreeLevelWardDocs().get(0);
+                List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDoc.getAllDoctorWradDocs();
+                if (allDoctorWradDocs.size() == 0) {
+                    status.set("0");
+                    return;
+                }
+                Date lastRecordDate = getLastRecordDate(allDoctorWradDocs);
+                if (lastRecordDate == null) {
+                    return;
+                }
+                int hoursPerWeek = 7 * 24 * 60;
+                String roundRecordThisWeek = "";
+                List<String> roundRecordEveryWeek = new ArrayList<>();
+                beginDate = DateUtil.dateZeroClear(beginDate);//从入院记录当天天0点开始算
+                int i = 1;
+                //长兴首次病程算一次主治查房,所以第一周只需要两次主治查房就可以
+                int rounds = 0;//主治查房次数
+                Date firstWeek = DateUtil.addDate(beginDate, 7);
+                for (AttendingDoctorWardDoc attendingDoctorWardDoc : threeLevelWardDoc.getAttendingDoctorWardDocs()) {//循环筛选主治,找出地日在第一周的个数
+                    String recordTime = attendingDoctorWardDoc.getStructureMap().get("查房日期");
+                    Date recordDate = StringUtil.parseDateTime(recordTime);
+                    if (recordDate.before(firstWeek)) {
+                        rounds = rounds + 1;
+                    }
+                }
+                if (rounds >= 2) {
+                    return;
+                }
+                String lastWardDateRange = "";
+                List<String> lastWardDateRangeList = new ArrayList<>();
+                //每周的病历记录
+                while (i >= 1) {
+                    roundRecordThisWeek = extractWardRecord(inputInfo, allDoctorWradDocs, beginDate, hoursPerWeek, lastRecordDate);
+                    if (CatalogueUtil.isEmpty(roundRecordThisWeek)) {
+                        break;
+                    }
+                    //如果6天后日期大于出院日期,跳过
+                    if (StringUtil.isBlank(dischargeTime)) {
+                        dischargeTime = presentTime;
+                    }
+                    Date sixDate = DateUtil.addDate(beginDate, 7);
+                    if (StringUtil.parseDateTime(dischargeTime).before(sixDate)) {
+                        break;
+                    }
+                    lastWardDateRange = DateUtil.formatDate(beginDate) + "    ->    " + DateUtil.formatDate(sixDate);
+                    lastWardDateRangeList.add(lastWardDateRange);
+                    roundRecordEveryWeek.add(roundRecordThisWeek);
+                    beginDate = DateUtil.addDate(beginDate, 7);
+                    i++;
+                }
+                if (roundRecordEveryWeek.size() == 0) {
+                    status.set("0");
+                    return;
+                }
+                boolean firstRecordAttendExist = findfirstRecordAttend(inputInfo);
+                List<String> resultInfos = new ArrayList<>();
+                for (int j = 0; j < roundRecordEveryWeek.size(); j++) {
+                    int indicationsNum = CatalogueUtil.appearNumber(roundRecordEveryWeek.get(j).split(","), Content.attend);
+                    //三级医师查房算一次主治查房
+                    int threeDoctorNum = CatalogueUtil.appearNumber(roundRecordEveryWeek.get(j).split(","), "三级");
+                    if (j == 0 && firstRecordAttendExist) { //如果首程中医师签名为主治医师,第一周查房记录也要加上
+                        indicationsNum += 1;
+                    }
+                    if (indicationsNum + threeDoctorNum < 3) {
+                        //每周无3次主治医师查房记录
+                        status.set("-1");
+                        resultInfos.add(lastWardDateRangeList.get(j));
+                    }
+                }
+
+                if (resultInfos.size() > 0) {
+                    info.set(StringUtils.join(resultInfos.toArray(), ";"));
+                }
+            }
+        }
+    }
+
+    private Date getLastRecordDate(List<ThreeLevelWardDoc> allDoctorWradDocs) {
+        ThreeLevelWardDoc threeLevelWardDoc = allDoctorWradDocs.get(allDoctorWradDocs.size() - 1);
+        Map<String, String> lastWardDocStructureMap = threeLevelWardDoc.getStructureMap();
+        String wardDateStr = lastWardDocStructureMap.get("查房日期");
+        if (StringUtil.isNotBlank(wardDateStr)) {
+            return StringUtil.parseDateTime(wardDateStr);
+        }
+        return null;
+    }
+
+
+    /**
+     * 抽取duration分钟内所有查房标题
+     * 抽取一周内所有查房标题,若一周内记录少于6天,则返回""
+     *
+     * @param threeLevelWardDocs
+     * @param admisDate
+     * @param duration
+     * @return
+     */
+    private static String extractWardRecord(InputInfo inputInfo, List<ThreeLevelWardDoc> threeLevelWardDocs, Date admisDate, int duration, Date maxRecordDate) {
+        String recordTime = "", recordTitle = "", title = "";
+        List<Date> dateList = new ArrayList();
+        for (ThreeLevelWardDoc threeLevelWardDoc : threeLevelWardDocs) {
+            Map<String, String> threeLevelWardStructureMap = threeLevelWardDoc.getStructureMap();
+            recordTime = threeLevelWardStructureMap.get("查房日期");
+            title = threeLevelWardStructureMap.get("查房标题");
+            if (StringUtil.isBlank(recordTime) || StringUtil.isBlank(title)) {
+                continue;
+            }
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null) {
+                continue;
+            }
+            /* 替换查房标题中主刀/一助的职称 */
+            List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+            if (title.contains("主刀") || title.contains("术后第")) {
+                String doctorTitle = CatalogueUtil.getDoctorTitle(operationDocs, recordDate, "主刀医师");
+                title = title.replace("主刀", doctorTitle).replace("术后第", doctorTitle);
+            } else if (title.contains("一助")) {
+                String doctorTitle = CatalogueUtil.getDoctorTitle(operationDocs, recordDate, "一助");
+                title = title.replace("一助", doctorTitle);
+            }
+            if (admisDate.before(recordDate) && !CatalogueUtil.compareTime(admisDate, DateUtil.dateZeroClear(recordDate), (long) duration)) {
+                recordTitle += title + ",";
+                dateList.add(recordDate);
+            }
+        }
+        if (dateList.size() > 0) {
+            //dateList.sort(Date::compareTo);
+            if (!maxRecordDate.equals(dateList.get(dateList.size() - 1)) || CatalogueUtil.compareTime(admisDate, dateList.get(dateList.size() - 1), Long.valueOf(6 * 24 * 60))) {
+                return recordTitle;
+            }
+        }
+        return "";
+    }
+
+    private boolean findfirstRecordAttend(InputInfo inputInfo) {
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        if (firstCourseRecordDoc == null) {
+            return false;
+        }
+        Map<String, Map<String, Object>> hospitalDoctorMap = specialStorageUtil.getJsonStringValue(KernelConstants.HOSPITAL_DOCTOR_MAP);
+        String doctorSign = firstCourseRecordDoc.getStructureMap().get("医师签名");
+        if (hospitalDoctorMap == null || StringUtil.isBlank(doctorSign)) {
+            return false;
+        }
+        if (hospitalDoctorMap.containsKey(doctorSign)) {
+            Object professor = hospitalDoctorMap.get(doctorSign).get("professor");
+            if (professor != null) {
+                return professor.toString().contains("主治") || professor.toString().contains("主任");
+            }
+        }
+        return false;
+    }
+}

+ 116 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0144.java

@@ -0,0 +1,116 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.*;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR0144
+ * @Description : 患者出院前无上级医师(主治及以上)同意出院的病程记录
+ * @Author : 胡敬
+ * @Date: 2020-03-19 17:20
+ */
+@Component
+public class THR0144 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            String admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            //出院日期
+            String dischargeTime = medicalRecordInfoDoc.getStructureMap().get("leaveHospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime) || CatalogueUtil.isEmpty(dischargeTime)) {
+                status.set("0");
+                return;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(admisTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(48 * 60))) {//如果入院未超过48小时,规则不判断
+                status.set("0");
+                return;
+            }
+            //如果住院天数小于2天则不判断该条规则
+            if (DateUtil.parseDate(dischargeTime) != null &&
+                    !CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(dischargeTime), (long) (48 * 60))) {
+                status.set("0");
+                return;
+            } else {
+                if (inputInfo.getThreeLevelWardDocs().size() == 0) {
+                    return;
+                }
+            }
+        }
+
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+        DeathCaseDiscussDoc deathCaseDiscussDoc = inputInfo.getDeathCaseDiscussDoc();
+        /* 如果没有出院小结,就不报         如果有死亡记录、死亡讨论记录,也不报 */
+        if (leaveHospitalDoc == null || deathRecordDoc != null || deathCaseDiscussDoc != null) {
+            status.set("0");
+            return;
+        }
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();
+        if (allDoctorWradDocs.size() == 0) {
+            status.set("0");
+            return;
+        }
+        int j = 0; //最后两次查房记录
+        Boolean lastFlag = true;
+        for (int i = allDoctorWradDocs.size() - 1; i >= 0 && j < 2; i--) {
+            j++;
+            ThreeLevelWardDoc lastWardDoc = allDoctorWradDocs.get(i);
+            Map<String, String> structureMap = lastWardDoc.getStructureMap();
+            String conditionRecord = structureMap.get("病情记录");
+            String treatmentPlan = structureMap.get("治疗计划和措施");
+            String title = structureMap.get("查房标题");
+            if (StringUtil.isBlank(title) || title.contains("病理报告")) {
+                continue;
+            }
+            conditionRecord = StringUtil.isBlank(conditionRecord) ? "" : conditionRecord;
+            conditionRecord = conditionRecord.replace(" ", "");
+            treatmentPlan = StringUtil.isBlank(treatmentPlan) ? "" : treatmentPlan;
+            //主任或主治查房 标题需要包含“主任”或“主治”,内容需要包含“出院”
+            if ((conditionRecord.contains("出院") || treatmentPlan.contains("出院") || conditionRecord.contains("转上级医院"))
+                    && (CatalogueUtil.subTitle(title).contains(Content.attend)
+                    || CatalogueUtil.subTitle(title).contains(Content.director)
+                    || CatalogueUtil.subTitle(title).contains("主刀"))) {
+                status.set("0");
+                return;
+            }
+            //普通查房 内容需要包含“上级”和“出院”
+            if (StringUtil.isNotBlank(title)
+                    && (title.contains("日常查房记录") || title.contains("普通查房记录") || title.contains("日常病程记录"))
+                    && (title.contains("上级") || conditionRecord.contains("上级"))
+                    && (conditionRecord.contains("出院") || treatmentPlan.contains("出院"))) {
+                status.set("0");
+                return;
+            }
+            // =====添加三级医师查房算出院查房=====
+            if (StringUtil.isNotBlank(title)&& (title.contains("三级"))
+                    && (conditionRecord.contains("出院") || treatmentPlan.contains("出院"))) {
+                status.set("0");
+                return;
+            }
+
+            // 添加硬规则,最后一个记录包含“医院”或 “离院”就不报错
+            if (lastFlag == true) {
+                if (conditionRecord.contains("医院") || conditionRecord.contains("离院") || conditionRecord.contains("出院")) {
+                    status.set("0");
+                    return;
+                }
+                lastFlag = false;
+            }
+        }
+    }
+}

+ 138 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02900.java

@@ -0,0 +1,138 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+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.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR02900
+ * @Description : 术后24小时内无主刀或一助查房记录
+ * @Author : 胡敬
+ * @Date: 2020-05-27 14:23
+ */
+@Component
+public class THR02900 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getOperationDocs().size() == 0) {
+            return;
+        }
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        List<ThreeLevelWardDoc> allDoctorWradDocs = new ArrayList<>();
+        if (inputInfo.getThreeLevelWardDocs() != null && inputInfo.getThreeLevelWardDocs().size() > 0) {
+            allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();
+        }
+        //宁海第一人民医院存在术后24小时模板
+        if (ListUtil.isNotEmpty(allDoctorWradDocs)) {
+            for (ThreeLevelWardDoc wardDoc : allDoctorWradDocs) {
+                Map<String, String> wardStructureMap = wardDoc.getStructureMap();
+                String recordTitle = wardStructureMap.get("查房标题");
+                if (StringUtil.isNotBlank(recordTitle) && recordTitle.contains("术后24小时")) {
+                    return;
+                }
+            }
+        }
+        //标题
+        String allTitle = "";
+        //病情记录
+        String allPathography = "";
+        String[] split = null;
+        //一助或助手
+        String firstAssistant = "";
+        //手术结束时间
+        String opeEndTime = "";
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> structureMap = operationRecordDoc.getStructureMap();
+            String opeAssName = structureMap.get("手术者及助手名称");
+            if (StringUtil.isNotBlank(opeAssName)) {
+                split = opeAssName.split("、");
+                firstAssistant = structureMap.get("助手");
+                opeEndTime = structureMap.get("手术日期");
+            } else {
+                firstAssistant = structureMap.get("一助");
+                //取得手术时间截取结束时间(格式 2020-07-24 10:30-2020-07-24 11:45)
+                opeEndTime = structureMap.get("手术日期");
+                if (StringUtil.isBlank(opeEndTime) || opeEndTime.contains("年月日")) {
+                    continue;
+                }
+            }
+            String chiefSurgeon = structureMap.get("主刀医师");
+            if (chiefSurgeon.contains("、")) {
+                chiefSurgeon = chiefSurgeon.split("、")[0];
+            }
+            Date opeEndDate = CatalogueUtil.parseStringDate(opeEndTime);
+            if (opeEndDate == null) {
+                continue;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(opeEndTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(24 * 60))) {//如果接收未超过6小时,规则不判断
+                return;
+            } else {
+                if ((StringUtil.isNotEmpty(chiefSurgeon) || StringUtil.isNotEmpty(firstAssistant)) && ListUtil.isEmpty(allDoctorWradDocs)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+            if (ListUtil.isNotEmpty(allDoctorWradDocs)) {
+                for (ThreeLevelWardDoc wardDoc : allDoctorWradDocs) {
+                    Map<String, String> wardStructureMap = wardDoc.getStructureMap();
+                    String recordDateStr = wardStructureMap.get("查房日期");
+                    String recordTitle = wardStructureMap.get("查房标题");
+                    String writTitle = wardStructureMap.get("文书标题");
+                    String pathography = wardStructureMap.get("病情记录");
+                    Date recordDate = CatalogueUtil.parseStringDate(recordDateStr);
+                    if (StringUtil.isBlank(recordTitle) || StringUtil.isBlank(pathography) || recordDate == null) {
+                        continue;
+                    }
+                    if (opeEndDate.before(recordDate) && !CatalogueUtil.compareTime(opeEndDate, recordDate, 24 * 60L)) {
+                        allTitle += recordTitle;
+                        allPathography += pathography;
+                        if (StringUtil.isNotBlank(writTitle)) {
+                            allTitle += writTitle;
+                        }
+                    }
+                }
+            }
+            //查房标题中有主刀
+            if (allTitle.contains("主刀") || allTitle.contains("术后第一天") || allTitle.contains("术后第1天")) {
+                return;
+            }
+            //病情记录对比
+            if ((StringUtil.isNotBlank(chiefSurgeon) && allPathography.contains(chiefSurgeon)) || allPathography.contains("术后第一天") || allPathography.contains("术后第1天") ||
+                    (StringUtil.isNotBlank(firstAssistant) && allPathography.contains(firstAssistant))) {
+                return;
+            }
+            //查房标题对比
+            if (StringUtil.isNotBlank(chiefSurgeon) && !allTitle.contains(chiefSurgeon) &&
+                    StringUtil.isNotBlank(firstAssistant) && !allTitle.contains(firstAssistant)) {
+                status.set("-1");
+                return;
+            } else if (StringUtil.isBlank(chiefSurgeon) && StringUtil.isBlank(firstAssistant) && split.length > 1) {
+                for (int i = 0; i < split.length; i++) {
+                    if (allTitle.contains(split[i])) {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}

+ 69 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02931.java

@@ -0,0 +1,69 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.DeathCaseDiscussDoc;
+import com.lantone.qc.pub.model.doc.DeathRecordDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR02931
+ * @Description : 最后一次查房记录时间晚于患者出院时间
+ * @Author : 胡敬
+ * @Date: 2020-06-16 10:06
+ */
+@Component
+public class THR02931 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        if (inputInfo.getThreeLevelWardDocs().size() == 0 || inputInfo.getLeaveHospitalDoc() == null) {
+            return;
+        }
+        DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+        DeathCaseDiscussDoc deathCaseDiscussDoc = inputInfo.getDeathCaseDiscussDoc();
+        if (deathRecordDoc != null || deathCaseDiscussDoc != null) {
+            return;
+        }
+        String leaveDateStr = null;
+        if (inputInfo.getMedicalRecordInfoDoc() != null) {
+            Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+            leaveDateStr = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+        }
+        if (StringUtil.isBlank(leaveDateStr)) {
+            return;
+        }
+        Date leaveDate = StringUtil.parseDateTime(leaveDateStr);
+        if (leaveDate == null) {
+            return;
+        }
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();
+        if (allDoctorWradDocs.size() == 0) {
+            return;
+        }
+        int j = 0; //最后两次查房记录
+        for (int i = allDoctorWradDocs.size() - 1; i > 0 && j < 2; i--) {
+            j++;
+            ThreeLevelWardDoc lastWardDoc = allDoctorWradDocs.get(i);
+            Map<String, String> structureMap = lastWardDoc.getStructureMap();
+            String title = structureMap.get("查房标题");
+            if (StringUtil.isBlank(title) || title.contains("病理报告")) {
+                continue;
+            }
+            String recordDateStr = structureMap.get("查房日期");
+            if (StringUtil.isNotBlank(recordDateStr)) {
+                Date recordDate = StringUtil.parseDateTime(recordDateStr);
+                if (recordDate != null && recordDate.after(leaveDate)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+}

+ 508 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02985.java

@@ -0,0 +1,508 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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.*;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+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.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : THR02985
+ * @Description : 医嘱有抗生素使用病程无记录
+ * 药品类型(0.普药 1.抗生素 2.激素)
+ * @Author : 胡敬
+ * @Date: 2020-06-23 10:04
+ */
+@Component
+public class THR02985 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();
+        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) {
+            return;
+        }
+
+        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
+                .stream()
+                .filter(Objects::nonNull)
+                .map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("抗生素"))
+//                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+
+        docAdvStruct.removeIf(x -> StringUtil.isNotBlank(x.get("给药方式")) && !filterKey.contains(x.get("给药方式")));
+        //记录同一天内是否开过多次同一抗生素
+        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
+        //记录同一抗生素同一天内是否开过多次,用于医嘱中需要处理的抗生素过滤(一天内同一抗生素开过多次的抗生素直接过滤)
+        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
+        Map<Date, String> doctorAdviceDrugMap = Maps.newLinkedHashMap();
+        Date startDate = null;
+        for (Map<String, String> adviceDoc : docAdvStruct) {
+            String drugName = adviceDoc.get("医嘱项目名称");
+            String startDateStr = adviceDoc.get("医嘱开始时间");
+            if (StringUtil.isNotBlank(drugName)) {
+                startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+//                startDate = StringUtil.parseDateTime(startDateStr);
+                if (antibioticDateTimes.get(drugName).get(startDate) > 0) {
+                    continue;   //一天内同一抗生素开过多次的抗生素直接过滤
+                }
+                drugName = removeBracket(drugName).replaceAll("[^\u4e00-\u9fa5]", "");
+                String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    drugName = drugStandardWord;
+                }
+                if (Arrays.asList(KSS).contains(drugName)) {
+                    doctorAdviceDrugMap.put(startDate, adviceDoc.get("医嘱项目名称"));
+                }
+            }
+        }
+
+        Map<String, Date> info = Maps.newLinkedHashMap();
+        Map<String, List<Drug>> infoModel = Maps.newLinkedHashMap();
+        String dateStr = null;
+        //入院记录中获取信息
+        /*if (beHospitalizedDoc != null) {
+            Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+            getInfo(info, structureMap, "入院记录", "入院日期", "治疗计划");
+        }*/
+        //从首程治疗计划中获取信息
+        if (firstCourseRecordDoc != null) {
+            getInfo(info, firstCourseRecordDoc.getStructureMap(), "首次病程录", "病历日期", "诊疗计划");
+            if (firstCourseRecordDoc.getDrugLabel() != null) {
+                List<Drug> drugs = firstCourseRecordDoc.getDrugLabel().getDrugs();
+                dateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
+                if (StringUtil.isNotBlank(dateStr)) {
+                    getInfo(infoModel, dateStr, drugs);
+                }
+            }
+        }
+
+        //从查房记录中获取信息
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            List<ThreeLevelWardDoc> wardDocs = allDoctorWradDocs
+                    .stream()
+                    .filter(x -> StringUtil.isNotBlank(x.getStructureMap().get("查房日期")) && x.getThreeLevelWardLabel().size() > 0)
+                    .collect(Collectors.toList());
+            wardDocs.forEach(x -> getInfo(info, x.getStructureMap(), "查房记录", "查房日期", "病情记录", "治疗计划和措施"));
+            wardDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("查房日期"), x.getThreeLevelWardLabel().get(x.getThreeLevelWardLabel().size() - 1).getDrugs()));
+        }
+        //从手术记录中获取信息
+        if (operationDocs.size() > 0) {
+            //手术记录
+            List<OperationRecordDoc> operationRecordDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationRecordDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationRecordLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("手术时间")))
+                    .collect(Collectors.toList());
+            operationRecordDocs.forEach(x -> getInfo(info, x.getStructureMap(), "手术记录", "手术时间", "手术经过及处理"));
+            operationRecordDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("手术时间"), x.getOperationRecordLabel().getDrugs()));
+            //术后首程
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("病历日期")))
+                    .collect(Collectors.toList());
+            operationDiscussionDocs.forEach(x -> getInfo(info, x.getStructureMap(), "术后首程", "病历日期", "手术简要经过", "术后处理措施"));
+            operationDiscussionDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("病历日期"), x.getOperationDiscussionLabel().getDrugs()));
+        }
+
+        //从会诊记录中获取信息
+        /*if (consultationDocs.size() > 0) {
+            List<ConsultationResultsDoc> consultationResultsDocs = consultationDocs
+                    .stream()
+                    .map(ConsultationDoc::getConsultationResultsDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getConsultationResultLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("会诊日期及时间")))
+                    .collect(Collectors.toList());
+            consultationResultsDocs.forEach(x -> getInfo(info, x.getStructureMap(), "会诊结果单", "会诊日期及时间", "会诊意见"));
+            consultationResultsDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("会诊日期及时间"), x.getConsultationResultLabel().getDrugs()));
+        }*/
+
+        //从出院小结中获取信息
+        if (leaveHospitalDoc != null) {
+            if (inputInfo.getMedicalRecordInfoDoc() != null) {
+                Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+                dateStr = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+                //如果存在出院小结,出院日期为空,存储系统当前时间
+                if (StringUtil.isBlank(dateStr)) {
+                    dateStr = DateUtil.formatDateTime(new Date());
+                }
+            }
+            if (StringUtil.isNotBlank(dateStr)) {
+                if (leaveHospitalDoc.getLeaveHospitalLabel() != null) {
+                    List<Drug> drugs = leaveHospitalDoc.getLeaveHospitalLabel().getDrugs();
+                    getDischargeInfo(info, leaveHospitalDoc.getStructureMap(), "出院小结", dateStr, "诊治经过");
+                    getInfo(infoModel, dateStr, drugs);
+                }
+            }
+        }
+
+        StringBuffer sb = new StringBuffer();
+        Map<Object, Object> data = Maps.newHashMap();
+        Set<String> existDrug = Sets.newHashSet();
+        String infoStr = "";
+        for (Map.Entry<Date, String> doctorAdviceDrug : doctorAdviceDrugMap.entrySet()) {
+            Date doctorAdviceDate = doctorAdviceDrug.getKey();
+            String drugs = doctorAdviceDrug.getValue();
+            if (drugs.contains(" ")) {
+                drugs = drugs.split(" ")[0];
+            }
+//            drugs = removeBracket(drugs).replaceAll("[^\u4e00-\u9fa5]", "");
+            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, existDrug);
+                //当前抗生素药(drugs)在info中已找到,直接跳出当前循环
+                if (StringUtil.isBlank(missDrug)) {
+                    break;
+                }
+            }
+            if (StringUtil.isBlank(missDrug)) {
+                continue;
+            }
+            /**********************************************文本匹配没有找到药,再走模型************************************************/
+            boolean modelFind = false;
+            for (Map.Entry<String, List<Drug>> modelMap : infoModel.entrySet()) {
+                dateStr = modelMap.getKey();
+                List<Drug> diags = modelMap.getValue();
+                Date date = StringUtil.parseDateTime(dateStr);
+                //医嘱开始时间往后2天或往前一天找药
+                if ((doctorAdviceDate.before(date) && !CatalogueUtil.compareTime(doctorAdviceDate, date, 48 * 60L))
+                        || (date.before(doctorAdviceDate) && !CatalogueUtil.compareTime(date, doctorAdviceDate, 24 * 60L))) {
+                    for (String adDrug : splitDrugs) {
+                        for (Drug courseDrug : diags) {
+                            if (compareStandard(courseDrug.getName(), adDrug)) {
+                                modelFind = true;
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (modelFind) {
+                    break;
+                }
+            }
+
+            if (StringUtil.isNotBlank(missDrug) && !modelFind && CatalogueUtil.compareTime(doctorAdviceDate, new Date(), 48 * 60L)) {
+                infoAppend(sb, drugs, DateUtil.formatDateTime(doctorAdviceDate));
+                data.put(doctorAdviceDate, splitDrugs.toString().replaceAll("[\\[\\]]", ""));
+            }
+        }
+        if (StringUtil.isNotBlank(sb.toString())) {
+            this.status.set("-1");
+            this.info.set("医嘱:" + sb.toString().substring(0, sb.toString().length() - 1));
+            extData.set(data);
+        }
+    }
+
+    /**
+     * 记录同一抗生素同一天内是否开过多次,用于医嘱中需要处理的抗生素过滤(一天内同一抗生素开过多次的抗生素直接过滤)
+     *
+     * @param docAdvStruct
+     * @param antibioticDateTimes
+     */
+    private void getAntibioticTimes(List<Map<String, String>> docAdvStruct, Map<String, Map<Date, Integer>> antibioticDateTimes) {
+        String drugName;
+        String startDateStr;
+        Date startDate;
+        Map<Date, Integer> antibioticDateTime;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+//            startDate = StringUtil.parseDateTime(startDateStr);
+            if (antibioticDateTimes.containsKey(drugName)) {
+                Map<Date, Integer> map = antibioticDateTimes.get(drugName);
+                if (map.containsKey(startDate)) {
+                    map.put(startDate, map.get(startDate) + 1);
+                } else {
+                    map.put(startDate, 0);
+                }
+            } else {
+                antibioticDateTime = Maps.newHashMap();
+                antibioticDateTime.put(startDate, 0);
+                antibioticDateTimes.put(drugName, antibioticDateTime);
+            }
+        }
+    }
+
+    /**
+     * 获取各模块信息<入院记录、首次病程录、手术记录、术后首程、会诊结果单、查房记录>
+     *
+     * @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);
+            }
+        }
+    }
+
+    /**
+     * 获取出院小结模块信息
+     *
+     * @param structureMap
+     * @param info
+     */
+    private void getDischargeInfo(Map<String, Date> info, Map<String, String> structureMap, String modelType, String dateStr, String... contentKey) {
+        String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList(contentKey));
+        if (StringUtil.isNotBlank(dateStr)) {
+            Date date = StringUtil.parseDateTime(dateStr);
+            if (StringUtil.isNotBlank(content) && date != null) {
+                info.put(modelType + "->" + content, date);
+            }
+        }
+    }
+
+    private void getInfo(Map<String, List<Drug>> info, String dateKey, List<Drug> drugs) {
+        List<Drug> tempDrugs = new ArrayList<>();
+        if (null != drugs && drugs.size() > 0) {
+            for (Drug drug : drugs) {
+                if (null != drug.getConsumption() && !drug.getConsumption().getName().contains("mg/Kg")) {
+                    tempDrugs.add(drug);
+                }
+            }
+        }
+        if (info.containsKey(dateKey)) {
+            info.get(dateKey).addAll(tempDrugs);
+        } else {
+            info.put(dateKey, tempDrugs);
+        }
+    }
+
+    /**
+     * 核心:从文本中找药
+     *
+     * @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, Set<String> existDrug) {
+        if ("时间不匹配".equals(missDrug)) {
+            missDrug = "";//初始化缺失药物
+        }
+        //开医嘱时间起,昨天今天明天记录内查找药
+        if ((doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L))
+                || (wardDate.before(doctorAdviceDate) && !CatalogueUtil.compareTime(wardDate, doctorAdviceDate, 24 * 60L)) ||
+                DateUtils.isSameDay(wardDate, doctorAdviceDate)) {
+            boolean findDrug = false;
+            String standardDrug = null;
+            for (String drug : drugs) {
+                if (StringUtil.isBlank(drug)) {
+                    continue;
+                }
+                drug = drug.replaceAll("[^\\u4e00-\\u9fa5]", "");
+                standardDrug = similarityUtil.getDrugStandardWord(drug);
+                if (content.contains(drug) || (StringUtil.isNotBlank(content) && StringUtil.isNotBlank(standardDrug) && content.contains(standardDrug))
+                        || (existDrug.contains(drug) && (regexFind(content, "继续", "治疗") || regexFind(content, "维持", "治疗")
+                        || regexFind(content, "继续", "抗感染") || regexFind(content, "治疗", "同前") || regexFind(content, "治疗同前")))) {
+                    findDrug = true;
+                    existDrug.add(drug);
+                    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 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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(date).append(")").append("_");
+    }
+
+    private static final List<String> filterKey = Lists.newArrayList("ACF", "ID", "IG", "IM", "IP", "IV",
+            "关节腔注射", "宫颈注射", "皮下注射", "皮下注射(儿童)", "皮下注射(免费)", "皮下注射(成人)", "皮内", "皮内注射",
+            "结膜下注射", "肌注", "肌肉注射(儿童)", "肌肉注射(公卫专用)", "肌肉注射(成人)", "胸腔注射", "腹腔内注射", "腹腔注射",
+            "静滴(儿童)", "静滴(成人)", "静脉注射", "静脉注射(儿童)", "静脉注射(免费)", "静脉注射(成人)", "静脉注射(泵)",
+            "静脉滴注", "静脉滴注(泵)", "鞘内注射", "微泵");
+
+    private static final String[] KSS = {
+            "万古霉素",
+            "两性霉素B",
+            "亚胺培南西司他丁",
+            "伊曲康唑",
+            "伏立康唑",
+            "依替米星",
+            "克拉霉素",
+            "克林霉素",
+            "利奈唑胺",
+            "利奈唑胺葡萄糖",
+            "利福昔明",
+            "制霉菌素",
+            "卡泊芬净",
+            "厄他培南",
+            "吗啉硝唑",
+            "呋喃唑酮",
+            "哌拉西林他唑巴坦",
+            "磺胺甲恶唑",
+            "多粘菌素B",
+            "多西环素",
+            "夫西地酸",
+            "头孢丙烯",
+            "头孢他啶",
+            "头孢他啶阿维巴坦",
+            "头孢他美酯",
+            "头孢克洛",
+            "头孢克肟",
+            "头孢吡肟",
+            "头孢呋辛",
+            "头孢哌酮舒巴坦",
+            "头孢唑林",
+            "头孢噻肟",
+            "头孢地嗪",
+            "头孢地尼",
+            "头孢拉定",
+            "头孢曲松",
+            "头孢替安",
+            "头孢美唑",
+            "头孢西丁",
+            "奥硝唑",
+            "妥布霉素",
+            "妥布霉素地塞米松",
+            "左氧氟沙星",
+            "左氧氟沙星",
+            "庆大霉素",
+            "异帕米星",
+            "拉氧头孢",
+            "替加环素",
+            "替硝唑",
+            "替考拉宁",
+            "比阿培南",
+            "氟康唑",
+            "氟康唑",
+            "氟胞嘧啶",
+            "氨曲南",
+            "氨苄西林",
+            "泊沙康唑",
+            "特比萘芬",
+            "甲硝唑",
+            "甲硝唑",
+            "磷霉素",
+            "磷霉素氨丁三醇",
+            "米卡芬净",
+            "米诺环素",
+            "红霉素",
+            "美罗培南",
+            "苄星青霉素",
+            "莫西沙星",
+            "莫西沙星",
+            "达托霉素",
+            "阿奇霉素",
+            "阿奇霉素枸橼酸二氢钠",
+            "阿洛西林",
+            "阿米卡星",
+            "阿莫西林",
+            "阿莫西林克拉维酸",
+            "青霉素"
+    };
+}

+ 506 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR02986.java

@@ -0,0 +1,506 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+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.*;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+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.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : THR02986
+ * @Description : 医嘱有激素使用病程无记录
+ * 药品类型(0.普药 1.抗生素 2.激素)
+ * @Author : 胡敬
+ * @Date: 2020-06-23 10:43
+ */
+@Component
+public class THR02986 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();
+        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) {
+            return;
+        }
+        List<Map<String, String>> docAdvStruct = doctorAdviceDocs
+                .stream()
+                .filter(Objects::nonNull)
+                .map(DoctorAdviceDoc::getStructureMap)
+                .filter(x -> StringUtil.isNotBlank(x.get("药品类型")) && x.get("药品类型").contains("激素"))
+//                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                //                .filter(x -> StringUtil.isNotBlank(x.get("给药方式")) && x.get("给药方式").contains("静脉滴注"))
+                //                .filter(x -> StringUtil.isNotBlank(x.get("医嘱频率")) && !x.get("医嘱频率").equals("ONCE"))
+                .collect(Collectors.toList());
+
+        docAdvStruct.removeIf(x -> StringUtil.isNotBlank(x.get("给药方式")) && !filterKey.contains(x.get("给药方式")));
+
+        //记录同一天内是否开过多次同一激素
+        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
+        //记录同一激素同一天内是否开过多次,用于医嘱中需要处理的激素过滤(一天内同一激素开过多次的激素直接过滤)
+        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
+        Map<Date, String> doctorAdviceDrugMap = Maps.newLinkedHashMap();
+        Date startDate = null;
+        for (Map<String, String> adviceDoc : docAdvStruct) {
+            String drugName = adviceDoc.get("医嘱项目名称");
+            String startDateStr = adviceDoc.get("医嘱开始时间");
+            if (StringUtil.isNotBlank(drugName)) {
+                startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+//                startDate = StringUtil.parseDateTime(startDateStr);
+                if (antibioticDateTimes.get(drugName).get(startDate) > 0) {
+                    continue;   //一天内同一抗生素开过多次的抗生素直接过滤
+                }
+                drugName = removeBracket(drugName).replaceAll("[^\u4e00-\u9fa5]", "");
+                String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    drugName = drugStandardWord;
+                }
+                if (drugName.contains("甲泼尼龙") || drugName.contains("泼尼松") || drugName.contains("地塞米松") || drugName.contains("可的松")) {
+                    doctorAdviceDrugMap.put(startDate, adviceDoc.get("医嘱项目名称"));
+                }
+                //                if (!Arrays.asList(JS).contains(drugName)) {
+                //                     doctorAdviceDrugMap.put(startDate, adviceDoc.get("医嘱项目名称"));
+                //                }
+            }
+        }
+
+        Map<String, Date> info = Maps.newLinkedHashMap();
+        Map<String, List<Drug>> infoModel = Maps.newLinkedHashMap();
+        String dateStr = null;
+        //入院记录中获取信息
+        /*if (beHospitalizedDoc != null) {
+            Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+            getInfo(info, structureMap, "入院记录", "入院日期", "治疗计划");
+        }*/
+        //从首程治疗计划中获取信息
+        if (firstCourseRecordDoc != null) {
+            getInfo(info, firstCourseRecordDoc.getStructureMap(), "首次病程录", "病历日期", "诊疗计划");
+            if (firstCourseRecordDoc.getDrugLabel() != null) {
+                List<Drug> drugs = firstCourseRecordDoc.getDrugLabel().getDrugs();
+                dateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
+                if (StringUtil.isNotBlank(dateStr)) {
+                    getInfo(infoModel, dateStr, drugs);
+                }
+            }
+        }
+
+        //从查房记录中获取信息
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            List<ThreeLevelWardDoc> wardDocs = allDoctorWradDocs
+                    .stream()
+                    .filter(x -> StringUtil.isNotBlank(x.getStructureMap().get("查房日期")) && x.getThreeLevelWardLabel().size() > 0)
+                    .collect(Collectors.toList());
+            wardDocs.forEach(x -> getInfo(info, x.getStructureMap(), "查房记录", "查房日期", "病情记录", "治疗计划和措施"));
+            wardDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("查房日期"), x.getThreeLevelWardLabel().get(x.getThreeLevelWardLabel().size() - 1).getDrugs()));
+        }
+        //从手术记录中获取信息
+        if (operationDocs.size() > 0) {
+            //手术记录
+            List<OperationRecordDoc> operationRecordDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationRecordDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationRecordLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("手术时间")))
+                    .collect(Collectors.toList());
+            operationRecordDocs.forEach(x -> getInfo(info, x.getStructureMap(), "手术记录", "手术时间", "手术经过及处理"));
+            operationRecordDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("手术时间"), x.getOperationRecordLabel().getDrugs()));
+            //术后首程
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("病历日期")))
+                    .collect(Collectors.toList());
+            operationDiscussionDocs.forEach(x -> getInfo(info, x.getStructureMap(), "术后首程", "病历日期", "手术简要经过", "术后处理措施"));
+            operationDiscussionDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("病历日期"), x.getOperationDiscussionLabel().getDrugs()));
+        }
+
+        //从会诊记录中获取信息
+        /*if (consultationDocs.size() > 0) {
+            List<ConsultationResultsDoc> consultationResultsDocs = consultationDocs
+                    .stream()
+                    .map(ConsultationDoc::getConsultationResultsDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getConsultationResultLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("会诊日期及时间")))
+                    .collect(Collectors.toList());
+            consultationResultsDocs.forEach(x -> getInfo(info, x.getStructureMap(), "会诊结果单", "会诊日期及时间", "会诊意见"));
+            consultationResultsDocs.forEach(x -> getInfo(infoModel, x.getStructureMap().get("会诊日期及时间"), x.getConsultationResultLabel().getDrugs()));
+        }*/
+
+        //从出院小结中获取信息
+        if (leaveHospitalDoc != null) {
+            if (inputInfo.getMedicalRecordInfoDoc() != null) {
+                Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+                dateStr = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+                //如果存在出院小结,出院日期为空,存储系统当前时间
+                if (StringUtil.isBlank(dateStr)) {
+                    dateStr = DateUtil.formatDateTime(new Date());
+                }
+            }
+            if (StringUtil.isNotBlank(dateStr)) {
+                if (leaveHospitalDoc.getLeaveHospitalLabel() != null) {
+                    List<Drug> drugs = leaveHospitalDoc.getLeaveHospitalLabel().getDrugs();
+                    getDischargeInfo(info, leaveHospitalDoc.getStructureMap(), "出院小结", dateStr, "诊治经过");
+                    getInfo(infoModel, dateStr, drugs);
+                }
+            }
+        }
+
+        StringBuffer sb = new StringBuffer();
+        Map<Object, Object> data = Maps.newHashMap();
+        Set<String> existDrug = Sets.newHashSet();
+        String infoStr = "";
+        for (Map.Entry<Date, String> doctorAdviceDrug : doctorAdviceDrugMap.entrySet()) {
+            Date doctorAdviceDate = doctorAdviceDrug.getKey();
+            String drugs = doctorAdviceDrug.getValue();
+            if (drugs.contains(" ")) {
+                drugs = drugs.split(" ")[0];
+            }
+            //            drugs = removeBracket(drugs).replaceAll("[^\u4e00-\u9fa5]", "");
+            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()) {
+                doctorAdviceDate = DateUtil.dateZeroClear(doctorAdviceDate);
+                missDrug = getMissDrug(map.getKey(), map.getValue(), doctorAdviceDate, splitDrugs, 2, missDrug, existDrug);
+                //当前激素药(drugs)在info中已找到,直接跳出当前循环
+                if (StringUtil.isBlank(missDrug)) {
+                    break;
+                }
+            }
+            if (StringUtil.isBlank(missDrug)) {
+                continue;
+            }
+            /**********************************************文本匹配没有找到药,再走模型************************************************/
+            boolean modelFind = false;
+            for (Map.Entry<String, List<Drug>> modelMap : infoModel.entrySet()) {
+                dateStr = modelMap.getKey();
+                List<Drug> diags = modelMap.getValue();
+                Date date = StringUtil.parseDateTime(dateStr);
+                //医嘱开始时间往后2天或往前一天找药
+                if ((doctorAdviceDate.before(date) && !CatalogueUtil.compareTime(doctorAdviceDate, date, 48 * 60L))
+                        || (date.before(doctorAdviceDate) && !CatalogueUtil.compareTime(date, doctorAdviceDate, 24 * 60L))) {
+                    for (String adDrug : splitDrugs) {
+                        for (Drug courseDrug : diags) {
+                            if (compareStandard(courseDrug.getName(), adDrug)) {
+                                modelFind = true;
+                                break;
+                            }
+                        }
+                    }
+                }
+                if (modelFind) {
+                    break;
+                }
+            }
+            if (StringUtil.isNotBlank(missDrug) && !modelFind && CatalogueUtil.compareTime(doctorAdviceDate, new Date(), 48 * 60L)) {
+                infoAppend(sb, drugs, DateUtil.formatDateTime(doctorAdviceDrug.getKey()));
+                String drugStandardWord = similarityUtil.getDrugStandardWord(drugs);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    drugs = drugStandardWord;
+                }
+                data.put(doctorAdviceDrug.getKey(), drugs);
+            }
+        }
+        if (StringUtil.isNotBlank(sb.toString())) {
+            this.status.set("-1");
+            this.info.set("医嘱:" + sb.toString().substring(0, sb.toString().length() - 1));
+            extData.set(data);
+        }
+    }
+
+    /**
+     * 记录同一激素同一天内是否开过多次,用于医嘱中需要处理的激素过滤(一天内同一激素开过多次的激素直接过滤)
+     *
+     * @param docAdvStruct
+     * @param antibioticDateTimes
+     */
+    private void getAntibioticTimes(List<Map<String, String>> docAdvStruct, Map<String, Map<Date, Integer>> antibioticDateTimes) {
+        String drugName;
+        String startDateStr;
+        Date startDate;
+        Map<Date, Integer> antibioticDateTime;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+            // startDate = StringUtil.parseDateTime(startDateStr);
+            if (antibioticDateTimes.containsKey(drugName)) {
+                Map<Date, Integer> map = antibioticDateTimes.get(drugName);
+                if (map.containsKey(startDate)) {
+                    map.put(startDate, map.get(startDate) + 1);
+                } else {
+                    map.put(startDate, 0);
+                }
+            } else {
+                antibioticDateTime = Maps.newHashMap();
+                antibioticDateTime.put(startDate, 0);
+                antibioticDateTimes.put(drugName, antibioticDateTime);
+            }
+        }
+    }
+
+    /**
+     * 获取各模块信息<入院记录、首次病程录、手术记录、术后首程、会诊结果单、查房记录>
+     *
+     * @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);
+            }
+        }
+    }
+
+    /**
+     * 获取出院小结模块信息
+     *
+     * @param structureMap
+     * @param info
+     */
+    private void getDischargeInfo(Map<String, Date> info, Map<String, String> structureMap, String modelType, String dateStr, String... contentKey) {
+        String content = CatalogueUtil.structureMapJoin(structureMap, Lists.newArrayList(contentKey));
+        if (StringUtil.isNotBlank(dateStr)) {
+            Date date = StringUtil.parseDateTime(dateStr);
+            if (StringUtil.isNotBlank(content) && date != null) {
+                info.put(modelType + "->" + content, date);
+            }
+        }
+    }
+
+    private void getInfo(Map<String, List<Drug>> info, String dateKey, List<Drug> drugs) {
+        if (info.containsKey(dateKey)) {
+            info.get(dateKey).addAll(drugs);
+        } else {
+            info.put(dateKey, drugs);
+        }
+    }
+
+    /**
+     * 核心:从文本中找药
+     *
+     * @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, Set<String> existDrug) {
+        if ("时间不匹配".equals(missDrug)) {
+            missDrug = "";//初始化缺失药物
+        }
+        //开医嘱时间起,昨天今天明天记录内查找药
+        if ((doctorAdviceDate.before(wardDate) && !CatalogueUtil.compareTime(doctorAdviceDate, wardDate, days * 24 * 60L))
+                || (wardDate.before(doctorAdviceDate) && !CatalogueUtil.compareTime(wardDate, doctorAdviceDate, 24 * 60L))
+                || DateUtils.isSameDay(wardDate, doctorAdviceDate)) {
+            boolean findDrug = false;
+            String standardDrug = null;
+            for (String drug : drugs) {
+                if (StringUtil.isBlank(drug)) {
+                    continue;
+                }
+                drug = drug.replaceAll("[^\\u4e00-\\u9fa5]", "");
+                standardDrug = similarityUtil.getDrugStandardWord(drug);
+                if (content.contains(drug) || (StringUtil.isNotBlank(content) && StringUtil.isNotBlank(standardDrug) && content.contains(standardDrug))
+                        || (existDrug.contains(drug) && (regexFind(content, "继续", "治疗") || regexFind(content, "维持", "治疗")
+                        || regexFind(content, "继续", "抗感染") || regexFind(content, "治疗", "同前")))) {
+                    findDrug = true;
+                    existDrug.add(drug);
+                    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 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);
+        if (m.find()) {
+            String group = m.group();
+            if (group.contains("亚胺培南西司他丁针")) {
+                return false;
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * 比较两个激素标准词是否一致
+     *
+     * @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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(date).append(")").append("_");
+    }
+
+    private static final List<String> filterKey = Lists.newArrayList("静脉滴注", "静脉注射", "口服", "皮下注射", "肌注", "静脉注射(泵)",
+            "膀胱持续冲洗", "静脉滴注(泵)", "膀胱冲洗", "腹腔注射", "鞘内注射", "关节腔注射", "胸腔注射", "皮内");
+
+    /*private static final String[] JS = {
+            "瑞格列奈",
+            "西格列汀",
+            "维格列汀",
+            "伏格列波糖",
+            "二甲双胍",
+            "硫辛酸",
+            "阿卡波糖",
+            "格列齐特",
+            "达格列净",
+            "格列美脲",
+            "阿卡波糖",
+            "格列美脲",
+            "格列吡嗪",
+            "阿卡波糖",
+            "格列吡嗪",
+            "二甲双胍",
+            "吡格列酮二甲双胍",
+            "沙格列汀",
+            "吡格列酮",
+            "阿仑膦酸钠维D3",
+            "吡格列酮",
+            "硫辛酸",
+            "格列喹酮",
+            "阿仑膦酸钠",
+            "那格列奈",
+            "格列齐特",
+            "门冬胰岛素",
+            "甘精胰岛素",
+            "赖脯胰岛素",
+            "门冬胰岛素30",
+            "甲状腺",
+            "胰岛素",
+            "奥曲肽",
+            "赖脯胰岛素",
+            "生长抑素",
+            "特利加压素",
+            "奥曲肽",
+            "精蛋白生物合成人胰岛素",
+            "谷赖胰岛素",
+            "地特胰岛素",
+            "生长抑素",
+            "甲巯咪唑",
+            "鲑降钙素",
+            "赖脯胰岛素",
+            "利拉鲁肽",
+            "重组甘精胰岛素",
+            "精蛋白锌重组人胰岛素",
+            "赖脯胰岛素",
+            "黄体酮",
+            "地屈孕酮",
+            "重组人胰岛素",
+            "炔诺酮",
+            "特利加压素",
+            "戊酸雌二醇",
+            "重组人生长激素",
+            "生物合成人胰岛素",
+            "米非司酮",
+            "黄体酮",
+            "十一酸睾酮",
+            "精蛋白锌重组人胰岛素",
+            "丙硫氧嘧啶",
+            "黄体酮",
+            "重组人胰岛素",
+            "重组甘精胰岛素",
+            "鲑降钙素",
+            "甲羟孕酮",
+            "替勃龙",
+            "鲑降钙素",
+            "雷洛昔芬"
+    };*/
+}

+ 156 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03023.java

@@ -0,0 +1,156 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR03023
+ * @Description : 病重少于每2天记录1次
+ * @Author : zhoutg
+ * @Date: 2020-03-28 14:22
+ */
+@Component
+public class THR03023 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 1、有病重医嘱、查房记录。
+         * 2、第一个病重医嘱的开始时间当天有查房记录,结束时间有查房记录。
+         * 3、每2天有查房记录。
+         * 4、如果医嘱结束时间大于出院时间,那以出院时间为结束时间
+         * 5、首程也算第一次查房
+         */
+        status.set("0");
+        if (ListUtil.isEmpty(inputInfo.getDoctorAdviceDocs()) ||
+                ListUtil.isEmpty(inputInfo.getThreeLevelWardDocs())) {
+            return;
+        }
+
+        String leaveDateString = "";
+        Date leaveDate = null;
+        if (inputInfo.getLeaveHospitalDoc() != null) {
+            leaveDateString = inputInfo.getLeaveHospitalDoc().getStructureMap().get("出院时间");
+        } else if (inputInfo.getDeathRecordDoc() != null) {
+            leaveDateString = inputInfo.getDeathRecordDoc().getStructureMap().get("死亡时间");
+        }
+        if (StringUtils.isNotEmpty(leaveDateString)) {
+            leaveDate = StringUtil.parseDateTime(leaveDateString);
+        }
+
+        List<DoctorAdviceDoc> doctorAdviceDocList = inputInfo.getDoctorAdviceDocs();
+        List<List<Date>> doctorAdviceDate = new ArrayList<>();
+
+        // 目前只判断第一个病重医嘱
+        for (DoctorAdviceDoc bean : doctorAdviceDocList) {
+            Map<String, String> structureMap = bean.getStructureMap();
+            String name = structureMap.get("医嘱项目名称");
+            if (StringUtil.isNotBlank(name) && name.contains("病重")) {
+                String startDateStr = structureMap.get("医嘱开始时间");
+                String endDateStr = structureMap.get("医嘱结束时间");
+                if (StringUtil.isNotBlank(startDateStr) && StringUtil.isNotBlank(endDateStr)) {
+                    Date startDate = StringUtil.parseDateTime(startDateStr);
+                    Date endDate = StringUtil.parseDateTime(endDateStr);
+                    //如果医嘱结束时间大于出院时间,那以出院时间为结束时间
+                    if (leaveDate != null && leaveDate.before(endDate)) {
+                        endDate = leaveDate;
+                    }
+                    if (startDate.before(endDate)) {
+                        List<Date> listDate = new ArrayList<>();
+                        listDate.add(DateUtil.dateZeroClear(startDate));
+                        listDate.add(DateUtil.dateZeroClear(endDate));
+                        doctorAdviceDate.add(listDate);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (ListUtil.isEmpty(doctorAdviceDate)) {
+            return ;
+        }
+        List<Date> dateRecordDay = new ArrayList<>();
+
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();//查房记录
+        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+            Map<String, String> rescueStructureMap = threeLevelWardDoc.getStructureMap();
+            String recordTime = rescueStructureMap.get("查房日期");
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate != null) {
+                dateRecordDay.add(DateUtil.dateZeroClear(recordDate));
+            }
+        }
+
+        //首程也算第一次查房
+        if (inputInfo.getFirstCourseRecordDoc() != null) {
+            String recordTime = inputInfo.getFirstCourseRecordDoc().getStructureMap().get("病历日期");
+            if (StringUtils.isNotEmpty(recordTime)) {
+                Date recordDate = StringUtil.parseDateTime(recordTime);
+                dateRecordDay.add(DateUtil.dateZeroClear(recordDate));
+            }
+        }
+
+        if (ListUtil.isEmpty(dateRecordDay)) {
+            return ;
+        }
+
+        // 时间正序排列
+        Collections.sort(dateRecordDay, new Comparator<Date>() {
+            public int compare(Date o1, Date o2) {
+                int flag = o1.compareTo(o2);
+                return flag;
+            }
+        });
+
+        // 医嘱开始时间和结束时间当天必须要有查房记录
+        Date dateStart = dateRecordDay.get(0); // 查房第一个时间
+        Date dateEnd = dateRecordDay.get(dateRecordDay.size() - 1);// 查房最后一个时间
+        Date doctorDateStart = doctorAdviceDate.get(0).get(0); // 医嘱开始时间
+        Date doctorDateEnd = doctorAdviceDate.get(0).get(1); // 医嘱结束时间
+
+        // 医嘱开始时间当天有查房记录
+        if (!dateRecordDay.contains(doctorDateStart)) {
+            info.set(DateUtil.formatDate(doctorDateStart));
+            status.set("-1");
+            return ;
+        }
+        // 医嘱结束时间当天有查房记录
+        if (!dateRecordDay.contains(doctorDateEnd)) {
+            info.set(DateUtil.formatDate(doctorDateEnd));
+            status.set("-1");
+            return ;
+        }
+        int flag = 0;
+        Date nextDate = doctorDateStart;
+        int returnFlag = 0; // 防止出现死循环系统崩溃
+        String infoStr = "";
+        while(returnFlag <= 1000) {
+            if (!dateRecordDay.contains(nextDate)) {
+                flag++;
+            } else {
+                flag = 0;
+            }
+            if (flag == 2) {
+                status.set("-1");
+                infoStr = CatalogueUtil.concatInfo(infoStr, DateUtil.formatDate(nextDate));
+            }
+            nextDate = DateUtil.addDate(nextDate, 1);
+            if (nextDate.after(doctorDateEnd)) {
+                return ;
+            }
+            returnFlag++; // 防止出现死循环系统崩溃
+        }
+        if (StringUtils.isNotEmpty(infoStr)) {
+            info.set(infoStr);
+        }
+    }
+}

+ 148 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03024.java

@@ -0,0 +1,148 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR03024
+ * @Description : 病危没有每天或随时记录
+ * @Author : zhoutg
+ * @Date: 2020-03-28 14:22
+ */
+@Component
+public class THR03024 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        /**
+         * 1、有病危医嘱、查房记录。
+         * 2、第一个危重医嘱的开始时间当天有查房记录,结束时间有查房记录。
+         * 3、每天有查房记录。
+         * 4、如果医嘱结束时间大于出院时间,那以出院时间为结束时间
+         * 5、首程也算第一次查房
+         */
+        status.set("0");
+        if (ListUtil.isEmpty(inputInfo.getDoctorAdviceDocs()) ||
+                ListUtil.isEmpty(inputInfo.getThreeLevelWardDocs())) {
+            return;
+        }
+        String leaveDateString = "";
+        Date leaveDate = null;
+        if (inputInfo.getLeaveHospitalDoc() != null) {
+            leaveDateString = inputInfo.getLeaveHospitalDoc().getStructureMap().get("出院时间");
+        } else if (inputInfo.getDeathRecordDoc() != null) {
+            leaveDateString = inputInfo.getDeathRecordDoc().getStructureMap().get("死亡时间");
+        }
+        if (StringUtils.isNotEmpty(leaveDateString)) {
+            leaveDate = StringUtil.parseDateTime(leaveDateString);
+        }
+
+        List<DoctorAdviceDoc> doctorAdviceDocList = inputInfo.getDoctorAdviceDocs();
+        List<List<Date>> doctorAdviceDate = new ArrayList<>();
+
+        // 目前只判断第一个病重医嘱
+        for (DoctorAdviceDoc bean : doctorAdviceDocList) {
+            Map<String, String> structureMap = bean.getStructureMap();
+            String name = structureMap.get("医嘱项目名称");
+            if (StringUtil.isNotBlank(name) && name.contains("病危")) {
+                String startDateStr = structureMap.get("医嘱开始时间");
+                String endDateStr = structureMap.get("医嘱结束时间");
+                if (StringUtil.isNotBlank(startDateStr) && StringUtil.isNotBlank(endDateStr)) {
+                    Date startDate = StringUtil.parseDateTime(startDateStr);
+                    Date endDate = StringUtil.parseDateTime(endDateStr);
+                    //如果医嘱结束时间大于出院时间,那以出院时间为结束时间
+                    if (leaveDate != null && leaveDate.before(endDate)) {
+                        endDate = leaveDate;
+                    }
+                    if (startDate.before(endDate)) {
+                        List<Date> listDate = new ArrayList<>();
+                        listDate.add(DateUtil.dateZeroClear(startDate));
+                        listDate.add(DateUtil.dateZeroClear(endDate));
+                        doctorAdviceDate.add(listDate);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (ListUtil.isEmpty(doctorAdviceDate)) {
+            return ;
+        }
+        List<Date> dateRecordDay = new ArrayList<>();
+
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();//查房记录
+        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+            Map<String, String> rescueStructureMap = threeLevelWardDoc.getStructureMap();
+            String recordTime = rescueStructureMap.get("查房日期");
+            if (StringUtils.isNotEmpty(recordTime)) {
+                Date recordDate = StringUtil.parseDateTime(recordTime);
+                dateRecordDay.add(DateUtil.dateZeroClear(recordDate));
+            }
+        }
+        //首程也算第一次查房
+        if (inputInfo.getFirstCourseRecordDoc() != null) {
+            String recordTime = inputInfo.getFirstCourseRecordDoc().getStructureMap().get("病历日期");
+            if (StringUtils.isNotEmpty(recordTime)) {
+                Date recordDate = StringUtil.parseDateTime(recordTime);
+                dateRecordDay.add(DateUtil.dateZeroClear(recordDate));
+            }
+        }
+
+        if (ListUtil.isEmpty(dateRecordDay)) {
+            return ;
+        }
+
+        // 时间正序排列
+        Collections.sort(dateRecordDay, new Comparator<Date>() {
+            public int compare(Date o1, Date o2) {
+                int flag = o1.compareTo(o2);
+                return flag;
+            }
+        });
+
+        // 医嘱开始时间和结束时间当天必须要有查房记录
+        Date dateStart = dateRecordDay.get(0); // 查房第一个时间
+        Date dateEnd = dateRecordDay.get(dateRecordDay.size() - 1);// 查房最后一个时间
+        Date doctorDateStart = doctorAdviceDate.get(0).get(0); // 医嘱开始时间
+        Date doctorDateEnd = doctorAdviceDate.get(0).get(1); // 医嘱结束时间
+
+        // 医嘱开始时间当天有查房记录
+        if (!dateRecordDay.contains(doctorDateStart)) {
+            info.set(DateUtil.formatDate(doctorDateStart));
+            status.set("-1");
+            return ;
+        }
+        // 医嘱结束时间当天有查房记录
+        if (!dateRecordDay.contains(doctorDateEnd)) {
+            info.set(DateUtil.formatDate(doctorDateEnd));
+            status.set("-1");
+            return ;
+        }
+        Date nextDate = doctorDateStart;
+        int returnFlag = 0; // 防止出现死循环系统崩溃
+        String infoStr = "";
+        while(returnFlag <= 1000) {
+            if (!dateRecordDay.contains(nextDate)) {
+                status.set("-1");
+                infoStr = CatalogueUtil.concatInfo(infoStr, DateUtil.formatDate(nextDate));
+            }
+            nextDate = DateUtil.addDate(nextDate, 1);
+            if (nextDate.after(doctorDateEnd)) {
+                return ;
+            }
+            returnFlag++; // 防止出现死循环系统崩溃
+        }
+        if (StringUtils.isNotEmpty(infoStr)) {
+            info.set(infoStr);
+        }
+    }
+}

+ 325 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03044.java

@@ -0,0 +1,325 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
+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.LisDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : THR03044
+ * @Description : 抗生素使用指征不明确
+ * @Author : kwz
+ * @Date: 2020-07-23 14:16
+ */
+@Component
+public class THR03044 extends QCCatalogue {
+    public static Map<String, String> lisData = ImmutableMap.<String, String>builder()
+            .put("超敏C反应蛋白", ">10")
+            .put("降钙素CT(免疫)", ">0.25")
+            .put("尿=白细胞", ">13.2")
+            //.put("白细胞计数[电阻抗法]", "<0.4")
+            //.put("中性粒细胞绝对值", ">12")
+            //.put("中性粒细胞百分率", ">70%")
+            .put("血培养报阳时间", "")
+            //            .put("尿培养加菌落计数", "细菌生长")
+            .put("一般细菌涂片检查", "细菌生长")
+            //            .put("内毒素鲎定性试验", "阳性")
+            //            .put("常规药敏定性试验", "阳性")
+            //            .put("细菌抗体测定", "阳性")
+            .put("碱性磷酸酶[速率法]", ">80")
+            .put("血沉", ">25")
+            //            .put("肥达氏反应", "大于等于1:160")
+            //            .put("外斐氏反应", "大于等于:160")
+            .put("粪=白细胞", ">2")
+            //            .put("前列腺液白细胞", "大于1个/高位视野")
+            .put("胸水常规=白细胞", ">500")
+            .put("G+球菌:痰夜", "少,中等,多,偶见")
+            .put("G-球菌:痰夜", "少,中等,多,偶见")
+            .put("G+杆菌:痰夜", "少,中等,多,偶见")
+            .put("G-杆菌:痰夜", "少,中等,多,偶见")
+            .build();
+
+    public static Map<String, String> lisGroupData = ImmutableMap.<String, String>builder()
+            .put("血常规=白细胞计数[电阻抗法]", ">12")
+            .put("血常规=中性粒细胞绝对数", ">7")
+            .put("血常规=中性粒百分数", ">70")
+            .build();
+
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        List<LisDoc> lisDocs = inputInfo.getLisDocs();
+
+        if (doctorAdviceDocs.size() == 0 || lisDocs.size() == 0) {
+            return;
+        }
+
+        Set<String> antibiotics = doctorAdviceDocs
+                .stream()
+                .map(x -> x.getStructureMap().get("药品类型"))
+                .filter(x -> StringUtil.isNotBlank(x) && x.contains("抗生素"))
+                .collect(Collectors.toSet());
+
+        if (antibiotics.size() == 0) {
+            return;
+        }
+
+        //体温大于37.4度也不报错
+        boolean fever = isFever(inputInfo);
+        if (fever) {
+            return;
+        }
+
+        //从入院记录辅检中提取化验判断
+        if (inputInfo.getBeHospitalizedDoc() != null) {
+            Map<String, String> behStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+            String pacs = CatalogueUtil.structureMapJoin(behStructureMap, Lists.newArrayList("辅助检查", "实验室检查", "专科检查"));
+            if (StringUtil.isNotBlank(pacs)) {
+                pacs = StringUtil.removeBlank(pacs);
+                List<String> regexWord = Lists.newArrayList("反应蛋白numbermg/L", "中性粒细胞number%", "白细胞number");
+                boolean firstMatch = false, secondMatch = false;
+                for (String regex : regexWord) {
+                    Pattern p = Pattern.compile(regex.replace("number", "[1-9]\\d*\\.?\\d*"));
+                    Matcher matcher = p.matcher(pacs);
+                    if (matcher.find()) {
+                        String lis = matcher.group(0);
+                        if (lis.contains("反应蛋白")) {
+                            double lisResult = Double.parseDouble(getNum(lis));
+                            if (lisResult < 10) {
+                                status.set("-1");
+                                return;
+                            }
+                        } else if (lis.contains("中性粒细胞")) {
+                            double lisResult = Double.parseDouble(getNum(lis));
+                            if (lisResult < 70) {
+                                firstMatch = true;
+                            }
+                        } else if (lis.contains("白细胞")) {
+                            double lisResult = Double.parseDouble(getNum(lis));
+                            if (lisResult < 7) {
+                                secondMatch = true;
+                            }
+                        }
+
+                    }
+                }
+                if (firstMatch || secondMatch) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+
+        /************************************************处理数据库中化验数据************************************************/
+        Map<String, String> lises = new HashMap<>();
+        Map<String, String> lisReportMap = new HashMap<>();
+
+        for (LisDoc lisDoc : lisDocs) {
+            Map<String, String> lisDocStructureMap = lisDoc.getStructureMap();
+            String lisName = lisDocStructureMap.get("报告名称").trim();
+            String result = lisDocStructureMap.get("检验结果");
+            lisReportMap.put(lisName, result);
+            if (lisName.contains("=")) {
+                lises.put(lisName.split("=")[1], result);
+            } else {
+                lises.put(lisName, result);
+            }
+        }
+
+
+        /**
+         * 白细胞组合项单独处理
+         */
+        if (lises.containsKey("白细胞计数[电阻抗法]")) {
+            String wbcValue = lisGroupData.get("血常规=白细胞计数[电阻抗法]");
+            String wbc = lises.get("白细胞计数[电阻抗法]");
+            if (!compare(wbc, wbcValue)) {
+                status.set("-1");
+                return;
+            }
+            if (lises.containsKey("中性粒细胞绝对数")) {
+                String neutrophilValue = lisGroupData.get("血常规=中性粒细胞绝对数");
+                String neutrophil = lises.get("中性粒细胞绝对数");
+                if (!compare(neutrophil, neutrophilValue)) {
+                    status.set("-1");
+                    return;
+                }
+            } else if (lises.containsKey("中性粒百分数")) {
+                String neutrophilValue = lisGroupData.get("血常规=中性粒百分数");
+                String neutrophil = lises.get("中性粒百分数");
+                if (!compare(neutrophil, neutrophilValue)) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+
+        //如果这个病例的化验项目不在lisData,不报
+        Set<String> dataLises = lisData.keySet();
+        Set<String> collect = new HashSet<>();
+        for (String dataLis : dataLises) {
+            if (dataLis.contains("=")) {
+                collect.add(dataLis.split("=")[1]);
+            } else {
+                collect.add(dataLis);
+            }
+        }
+        collect.retainAll(lises.keySet());
+        if (collect.size() == 0) {
+            return;
+        }
+
+        String[] lisNameMapSub = null;
+        String[] lisNameSub = null;
+        String lisMapKey = null, lisMapValue = null, lisKey = null, lisValue = null;
+        for (Map.Entry<String, String> lisDataMap : lisData.entrySet()) {
+            String lisName = lisDataMap.getKey();
+            String lisResult = lisDataMap.getValue();
+            if (lisName.contains("=")) {
+                lisNameMapSub = lisName.split("=");
+            }
+            if (lisNameMapSub != null) {
+                lisMapKey = lisNameMapSub[0]; //静态map(lisData)的key根据=分隔,得到的key,如尿=白细胞,lisMapKey=尿
+                lisMapValue = lisNameMapSub[1];//静态map(lisData)的key根据=分隔,得到的value,如尿=白细胞,lisMapValue=白细胞
+            } else {
+                lisMapValue = lisName;//若静态map(lisData)的key没有=,直接用key作为分隔后的value,如超敏C反应蛋白,lisMapValue=超敏C反应蛋白
+            }
+            for (Map.Entry<String, String> lis : lisReportMap.entrySet()) {
+                String lisname = lis.getKey();
+                String result = lis.getValue();
+                lisNameSub = lisname.split("=");
+                lisKey = lisNameSub[0];//库中化验表map的key根据=分隔,得到的key,如尿=白细胞,lisMapKey=尿
+                lisValue = lisNameSub[1];
+                if (StringUtil.isBlank(lisMapKey) && lisMapValue.equals(lisValue)) {
+                    if (!compare(result, lisResult)) {
+                        status.set("-1");
+                        return;
+                    }
+                }
+                if (StringUtil.isNotBlank(lisMapKey) && lisMapKey.contains(lisKey) && lisMapValue.equals(lisValue)) {
+                    if (!compare(result, lisResult)) {
+                        status.set("-1");
+                        return;
+                    }
+                }
+            }
+        }
+
+    }
+
+    /**
+     * 比对化验表数据与lisData中数据大小,若符合则返回true
+     *
+     * @param result
+     * @param value
+     * @return
+     */
+    private boolean compare(String result, String value) {
+        boolean flag = false;
+        String s = getNum(value);
+        String resultValue = getNum(result);
+        if (num_contain(resultValue) && num_contain(s)) {
+            if (Float.parseFloat(resultValue) > Float.parseFloat(s) && value.contains(">")) {
+                flag = true;
+            }
+            if (Float.parseFloat(resultValue) < Float.parseFloat(s) && value.contains("<")) {
+                flag = true;
+            }
+        } else {
+            if (s.contains(result)) {
+                flag = true;
+            }
+        }
+        return flag;
+    }
+
+    public String getNum(String content) {
+        //        String compile = "(\\d+\\.\\d+)|(\\d+)";
+        String group = "";
+        String compile = "([1-9]\\d*\\.?\\d*)|(0\\.\\d*[1-9])";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            group = matcher.group(0);
+        }
+        return group;
+    }
+
+    public Boolean num_contain(String content) {
+        //        String compile = "(\\d+\\.\\d+)|(\\d+)";
+        String compile = "(\\d+)";
+        Pattern p = Pattern.compile(compile);
+        Matcher matcher = p.matcher(content);
+        boolean b = matcher.find();
+        return b;
+    }
+
+    private boolean isFever(InputInfo inputInfo) {
+        double temp = 0d;
+        boolean rever = false;
+        //入院记录找体温是否大于37.4℃
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        if (beHospitalizedDoc != null) {
+            Map<String, String> beHStructureMap = beHospitalizedDoc.getStructureMap();
+            String temperature = beHStructureMap.get("体温");
+            if (StringUtil.isNotBlank(temperature)) {
+                temp = Double.parseDouble(getNum(temperature));
+            }
+            String special = StringUtil.isNotBlank(beHStructureMap.get("专科小结")) ? beHStructureMap.get("专科小结") : beHStructureMap.get("专科体格检查");
+            if (temp == 0 && StringUtil.isNotBlank(special)) {
+                temp = getTemperature(special);
+            }
+            if (temp > 37.4) {
+                return true;
+            }
+        }
+
+        //查房记录找体温是否大于37.4℃
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            List<String> content = allDoctorWradDocs
+                    .stream()
+                    .map(ThreeLevelWardDoc::getStructureMap)
+                    .map(x -> x.get("病情记录"))
+                    .collect(Collectors.toList());
+
+            for (String str : content) {
+                temp = getTemperature(str);
+                if (temp > 37.4) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * 从文本中取体温
+     *
+     * @param content 文本
+     * @return
+     */
+    private double getTemperature(String content) {
+        double temp = 0d;
+        Pattern p = Pattern.compile("[1-9]\\d*\\.?\\d*℃");
+        Matcher matcher = p.matcher(content);
+        if (matcher.find()) {
+            temp = Double.parseDouble(getNum(matcher.group(0)));
+        }
+        return temp;
+    }
+}

+ 300 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03069.java

@@ -0,0 +1,300 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.LeaveHospitalDoc;
+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.entity.Drug;
+import com.lantone.qc.pub.model.label.LeaveHospitalLabel;
+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.util.*;
+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();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.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("医嘱单次剂量")))
+                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+
+        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);
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没加用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+                antibioticValue.remove(as.getKey());
+            }
+        }
+        //抗生素加用过的集合如果为空,则一个抗生素都没有加用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //病程记录中抗生素及查房时间(包括加用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateCourse = Maps.newHashMap();
+        //病程记录中抗生素加用集合   key:抗生素名    value:  0:未加用,1及以上:加用次数
+        Map<String, Integer> antibioticStatusCourse = Maps.newHashMap();
+        //病程记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueCourse = Maps.newHashMap();
+        String dateStr = null;
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+                if (doc.getThreeLevelWardLabel().size() == 0) {
+                    continue;
+                }
+                dateStr = doc.getStructureMap().get("查房日期");
+                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+                List<Drug> drugs = label.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************术后首程********************************************************/
+        if (operationDocs.size() > 0) {
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null)
+                    .collect(Collectors.toList());
+            for (OperationDiscussionDoc discussionDoc : operationDiscussionDocs) {
+                dateStr = discussionDoc.getStructureMap().get("记录日期");
+                if (StringUtil.isBlank(dateStr)) {
+                    continue;
+                }
+                List<Drug> drugs = discussionDoc.getOperationDiscussionLabel().getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************出院大结********************************************************/
+        if (leaveHospitalDoc != null) {
+            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
+            dateStr = leaveHospitalDoc.getStructureMap().get("出院时间");
+            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = leaveHospitalLabel.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        //将病程日期排序
+        antibioticDateCourse.forEach((x, y) -> y.sort(Comparator.naturalOrder()));
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有加用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有加用时的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素加用时的时间往后两天内,查房记录中该抗生素加用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中加用过的抗生素,查房记录中没出现过,则该抗生素会报出来(存入miss,规则最后会把所有不符合的抗生素都报出来)
+         */
+        StringBuffer sb = new StringBuffer();
+        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();
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);//医嘱中该药品对应的所有用量
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            /*if (!antibioticDateCourse.containsKey(drugKey)) {
+                for (String date : ad.getValue()) {
+                    infoAppend(sb, drugKey, date);
+                }
+                continue;
+            }*/
+            if (antibioticDateCourse.containsKey(drugKey)) {
+                List<Double> antibioticValueWardList = antibioticValueCourse.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 = antibioticDateCourse.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) {
+                    infoAppend(sb, drugKey, change);
+                }
+            }
+        }
+
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set(sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 收集各模块药品信息
+     *
+     * @param antibioticDateWard   病程中抗生素使用所有时间
+     * @param antibioticStatusWard 病程中抗生素用量改变状态
+     * @param antibioticValueWard  病程中抗生素及用量
+     * @param dateStr              记录日期
+     * @param drugs                模型提取出的药品列表
+     */
+    private void getCourseDrugInfo(Map<String, List<String>> antibioticDateWard, Map<String, Integer> antibioticStatusWard,
+                                   Map<String, List<Double>> antibioticValueWard, String dateStr, List<Drug> drugs) {
+        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, dateStr);
+                }
+            }
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @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 (v > 100) {
+            v = v / 1000;
+        }
+        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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(DateUtil.formatDate(StringUtil.parseDateTime(date))).append(")").append(",");
+    }
+
+}

+ 300 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03072.java

@@ -0,0 +1,300 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.LeaveHospitalDoc;
+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.entity.Drug;
+import com.lantone.qc.pub.model.label.LeaveHospitalLabel;
+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.util.*;
+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();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.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("医嘱单次剂量")))
+                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+
+        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);
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没减用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+                antibioticValue.remove(as.getKey());
+            }
+        }
+        //抗生素减用过的集合如果为空,则一个抗生素都没有减用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //病程记录中抗生素及查房时间(包括减用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateCourse = Maps.newHashMap();
+        //病程记录中抗生素减用集合   key:抗生素名    value:  0:未减用,1及以上:减用次数
+        Map<String, Integer> antibioticStatusCourse = Maps.newHashMap();
+        //病程记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueCourse = Maps.newHashMap();
+        String dateStr = null;
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+                if (doc.getThreeLevelWardLabel().size() == 0) {
+                    continue;
+                }
+                dateStr = doc.getStructureMap().get("查房日期");
+                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+                List<Drug> drugs = label.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************术后首程********************************************************/
+        if (operationDocs.size() > 0) {
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null)
+                    .collect(Collectors.toList());
+            for (OperationDiscussionDoc discussionDoc : operationDiscussionDocs) {
+                dateStr = discussionDoc.getStructureMap().get("记录日期");
+                if (StringUtil.isBlank(dateStr)) {
+                    continue;
+                }
+                List<Drug> drugs = discussionDoc.getOperationDiscussionLabel().getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************出院小结********************************************************/
+        if (leaveHospitalDoc != null) {
+            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
+            dateStr = leaveHospitalDoc.getStructureMap().get("出院时间");
+            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = leaveHospitalLabel.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        //将病程日期排序
+        antibioticDateCourse.forEach((x, y) -> y.sort(Comparator.naturalOrder()));
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有减用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有减用时的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素减用时的时间往后两天内,查房记录中该抗生素减用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中减用过的抗生素,查房记录中没出现过,则该抗生素会报出来(存入miss,规则最后会把所有不符合的抗生素都报出来)
+         */
+        StringBuffer sb = new StringBuffer();
+        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();
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);//医嘱中该药品对应的所有用量
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            /*if (!antibioticDateCourse.containsKey(drugKey)) {
+                for (String date : ad.getValue()) {
+                    infoAppend(sb, drugKey, date);
+                }
+                continue;
+            }*/
+            if (antibioticDateCourse.containsKey(drugKey)) {
+                List<Double> antibioticValueWardList = antibioticValueCourse.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 = antibioticDateCourse.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) {
+                    infoAppend(sb, drugKey, change);
+                }
+            }
+        }
+
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set(sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 收集各模块药品信息
+     *
+     * @param antibioticDateWard   病程中抗生素使用所有时间
+     * @param antibioticStatusWard 病程中抗生素用量改变状态
+     * @param antibioticValueWard  病程中抗生素及用量
+     * @param dateStr              记录日期
+     * @param drugs                模型提取出的药品列表
+     */
+    private void getCourseDrugInfo(Map<String, List<String>> antibioticDateWard, Map<String, Integer> antibioticStatusWard,
+                                   Map<String, List<Double>> antibioticValueWard, String dateStr, List<Drug> drugs) {
+        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, dateStr);
+                }
+            }
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @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 (v > 100) {
+            v = v / 1000;
+        }
+        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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(DateUtil.formatDate(StringUtil.parseDateTime(date))).append(")").append(",");
+    }
+
+}

+ 296 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03074.java

@@ -0,0 +1,296 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.LeaveHospitalDoc;
+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.entity.Drug;
+import com.lantone.qc.pub.model.label.LeaveHospitalLabel;
+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.util.*;
+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();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.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("医嘱单次剂量")))
+                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+
+        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);
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没加用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+                antibioticValue.remove(as.getKey());
+            }
+        }
+        //抗生素加用过的集合如果为空,则一个抗生素都没有加用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //病程记录中抗生素及查房时间(包括加用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateCourse = Maps.newHashMap();
+        //病程记录中抗生素加用集合   key:抗生素名    value:  0:未加用,1及以上:加用次数
+        Map<String, Integer> antibioticStatusCourse = Maps.newHashMap();
+        //病程记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueCourse = Maps.newHashMap();
+        String dateStr = null;
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+                if (doc.getThreeLevelWardLabel().size() == 0) {
+                    continue;
+                }
+                dateStr = doc.getStructureMap().get("查房日期");
+                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+                List<Drug> drugs = label.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************术后首程********************************************************/
+        if (operationDocs.size() > 0) {
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null)
+                    .collect(Collectors.toList());
+            for (OperationDiscussionDoc discussionDoc : operationDiscussionDocs) {
+                dateStr = discussionDoc.getStructureMap().get("记录日期");
+                if (StringUtil.isBlank(dateStr)) {
+                    continue;
+                }
+                List<Drug> drugs = discussionDoc.getOperationDiscussionLabel().getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************出院小结********************************************************/
+        if (leaveHospitalDoc != null) {
+            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
+            dateStr = leaveHospitalDoc.getStructureMap().get("出院时间");
+            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = leaveHospitalLabel.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        //将病程日期排序
+        antibioticDateCourse.forEach((x, y) -> y.sort(Comparator.naturalOrder()));
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有加用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有加用时的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素加用时的时间往后两天内,查房记录中该抗生素加用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中加用过的抗生素,查房记录中没出现过,则该抗生素会报出来
+         */
+        StringBuffer sb = new StringBuffer();
+        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();
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);//医嘱中该药品对应的所有用量
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            /*if (!antibioticDateCourse.containsKey(drugKey)) {
+                for (String date : ad.getValue()) {
+                    infoAppend(sb, drugKey, date);
+                }
+                continue;
+            }*/
+            if (antibioticDateCourse.containsKey(drugKey)) {
+                List<Double> antibioticValueWardList = antibioticValueCourse.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 = antibioticDateCourse.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) {
+                    infoAppend(sb, drugKey, change);
+                }
+            }
+        }
+
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set(sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 收集各模块药品信息
+     *
+     * @param antibioticDateWard   病程中抗生素使用所有时间
+     * @param antibioticStatusWard 病程中抗生素用量改变状态
+     * @param antibioticValueWard  病程中抗生素及用量
+     * @param dateStr              记录日期
+     * @param drugs                模型提取出的药品列表
+     */
+    private void getCourseDrugInfo(Map<String, List<String>> antibioticDateWard, Map<String, Integer> antibioticStatusWard,
+                                   Map<String, List<Double>> antibioticValueWard, String dateStr, List<Drug> drugs) {
+        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, dateStr);
+            }
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @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 (v > 100) {
+            v = v / 1000;
+        }
+        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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(DateUtil.formatDate(StringUtil.parseDateTime(date))).append(")").append(",");
+    }
+
+}

+ 298 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03075.java

@@ -0,0 +1,298 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.LeaveHospitalDoc;
+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.entity.Drug;
+import com.lantone.qc.pub.model.label.LeaveHospitalLabel;
+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.util.*;
+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();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.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("医嘱单次剂量")))
+                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+
+        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);
+            collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+        }
+
+        //把抗生素没减用过的抗生素删除
+        for (Map.Entry<String, Integer> as : antibioticStatus.entrySet()) {
+            if (as.getValue() == 0) {
+                antibioticDate.remove(as.getKey());
+                antibioticValue.remove(as.getKey());
+            }
+        }
+        //抗生素减用过的集合如果为空,则一个抗生素都没有减用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //病程记录中抗生素及查房时间(包括减用过抗生素的时间)     key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateCourse = Maps.newHashMap();
+        //病程记录中抗生素减用集合   key:抗生素名    value:  0:未减用,1及以上:减用次数
+        Map<String, Integer> antibioticStatusCourse = Maps.newHashMap();
+        //病程记录中抗生素及各初始剂量     key:抗生素名    value:抗生素第一次使用时剂量
+        Map<String, List<Double>> antibioticValueCourse = Maps.newHashMap();
+        String dateStr = null;
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+                if (doc.getThreeLevelWardLabel().size() == 0) {
+                    continue;
+                }
+                dateStr = doc.getStructureMap().get("查房日期");
+                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+                List<Drug> drugs = label.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************术后首程********************************************************/
+        if (operationDocs.size() > 0) {
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null)
+                    .collect(Collectors.toList());
+            for (OperationDiscussionDoc discussionDoc : operationDiscussionDocs) {
+                dateStr = discussionDoc.getStructureMap().get("记录日期");
+                if (StringUtil.isBlank(dateStr)) {
+                    continue;
+                }
+                List<Drug> drugs = discussionDoc.getOperationDiscussionLabel().getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        /*********************************************出院小结********************************************************/
+        if (leaveHospitalDoc != null) {
+            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
+            dateStr = leaveHospitalDoc.getStructureMap().get("出院时间");
+            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = leaveHospitalLabel.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, antibioticStatusCourse, antibioticValueCourse, dateStr, drugs);
+            }
+        }
+        //将病程日期排序
+        antibioticDateCourse.forEach((x, y) -> y.sort(Comparator.naturalOrder()));
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有减用时的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:查房记录中该抗生素所有减用时的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中该抗生素初始使用时间也在这两天内,则满足一半。
+         * 4.医嘱中该抗生素减用时的时间往后两天内,查房记录中该抗生素减用时间也在这两天内,则满足条件,该抗生素通过该条规则
+         * 5.继续判断下一个抗生素
+         * 6.若医嘱中减用过的抗生素,查房记录中没出现过,则该抗生素会报出来(存入miss,规则最后会把所有不符合的抗生素都报出来)
+         */
+        StringBuffer sb = new StringBuffer();
+        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();
+            List<Double> antibioticValueList = antibioticValue.get(drugKey);//医嘱中该药品对应的所有用量
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            /*if (!antibioticDateCourse.containsKey(drugKey)) {
+                for (String date : ad.getValue()) {
+                    infoAppend(sb, drugKey, date);
+                }
+                continue;
+            }*/
+            if (antibioticDateCourse.containsKey(drugKey)) {
+                List<Double> antibioticValueWardList = antibioticValueCourse.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 = antibioticDateCourse.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) {
+                    infoAppend(sb, drugKey, change);
+                }
+            }
+        }
+
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set(sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 收集各模块药品信息
+     *
+     * @param antibioticDateWard   病程中抗生素使用所有时间
+     * @param antibioticStatusWard 病程中抗生素用量改变状态
+     * @param antibioticValueWard  病程中抗生素及用量
+     * @param dateStr              记录日期
+     * @param drugs                模型提取出的药品列表
+     */
+    private void getCourseDrugInfo
+    (Map<String, List<String>> antibioticDateWard, Map<String, Integer> antibioticStatusWard,
+     Map<String, List<Double>> antibioticValueWard, String dateStr, List<Drug> drugs) {
+        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, dateStr);
+            }
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @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 (v > 100) {
+            v = v / 1000;
+        }
+        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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(DateUtil.formatDate(StringUtil.parseDateTime(date))).append(")").append(",");
+    }
+
+}

+ 598 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03077.java

@@ -0,0 +1,598 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.LeaveHospitalDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+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.DrugLabel;
+import com.lantone.qc.pub.model.label.ThreeLevelWardLabel;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.time.DateUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * @author HUJING
+ * @create 2020-08-28 14:10
+ * @desc 病程中抗生素记录不规范
+ **/
+@Component
+public class THR03077 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();
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.size() == 0) {
+            return;
+        }
+        Map<Date, String> extData = null;
+        if (outputInfo.getResult().get("THR02985") != null) {
+            extData = (Map<Date, String>) outputInfo.getResult().get("THR02985").get("extData");
+        }
+
+        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("医嘱单次剂量")))
+//                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+        docAdvStruct.removeIf(x -> StringUtil.isNotBlank(x.get("给药方式")) && !filterKey.contains(x.get("给药方式")));
+
+        //抗生素及开医嘱时间(包括加用过抗生素的时间)     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();
+        //记录同一天内是否开过多次同一抗生素
+        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
+        String drugName = null, value = null, startDateStr = null;
+        Date startDate = null;
+        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+            if (StringUtil.isNotBlank(drugName)) {
+                if (antibioticDateTimes.get(drugName).get(startDate) > 0) {
+                    continue;   //一天内同一抗生素开过多次的抗生素直接过滤
+                }
+                drugName = removeBracket(drugName).replaceAll("[^\u4e00-\u9fa5]", "");
+                //相似度标准词抓取失败,增加暂时处理
+                if (drugName.equals("克林霉素磷酸酯注射液特丽仙")) {
+                    drugName = drugName.replace("特丽仙", "");
+                }
+                String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+                if (StringUtil.isNotBlank(drugStandardWord)) {
+                    drugName = drugStandardWord;
+                }
+
+                if (extData != null && extData.containsKey(startDate) && extData.get(startDate).equals(drugName)) {
+                    continue;   //THR02985  医嘱有抗生素使用病程无记录,规则中没报未记录的抗生素继续走这条规则,报未记录的抗生素过滤
+                }
+
+                if (Arrays.asList(KSS).contains(drugName)) {
+                    collectAntibioticInfo(antibioticDate, antibioticStatus, antibioticValue, drugName, value, startDateStr);
+                }
+            }
+        }
+
+        //把抗生素使用剂量没变化过的抗生素删除
+//        antibioticStatus.forEach((x, y) -> {
+//            if (y == 0) {
+//                antibioticDate.remove(x);
+//                antibioticValue.remove(x);
+//            }
+//        });
+        //把同一天内同一个抗生素开过多次的抗生素删除
+//        antibioticDateTimes.forEach((x, y) -> {
+//            if (y > 0) {
+//                antibioticDate.remove(x);
+//                antibioticValue.remove(x);
+//            }
+//        });
+        //抗生素加用过的集合如果为空,则一个抗生素都没有加用过,直接返回0
+        if (antibioticDate.size() == 0) {
+            return;
+        }
+
+        //病程记录中没有用量的抗生素及查房时间       key:抗生素名    "2020-08-20,2020-08-21 ..."
+        Map<String, List<String>> antibioticDateCourse = Maps.newHashMap();
+        String dateStr = null;
+        /*********************************************首程治疗计划********************************************************/
+        if (firstCourseRecordDoc != null) {
+            DrugLabel drugLabel = firstCourseRecordDoc.getDrugLabel();
+            dateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
+            if (drugLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = drugLabel.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, dateStr, drugs,
+                        CatalogueUtil.structureMapJoin(firstCourseRecordDoc.getStructureMap(), Lists.newArrayList("诊疗计划")));
+            }
+        }
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+                if (doc.getThreeLevelWardLabel().size() == 0) {
+                    continue;
+                }
+                dateStr = doc.getStructureMap().get("查房日期");
+                ThreeLevelWardLabel label = doc.getThreeLevelWardLabel().get(0);
+                List<Drug> drugs = label.getDrugs();
+                getCourseDrugInfo(antibioticDateCourse, dateStr, drugs,
+                        CatalogueUtil.structureMapJoin(doc.getStructureMap(), Lists.newArrayList("病情记录", "治疗计划和措施")));
+            }
+        }
+        /*********************************************手术记录、术后首程************************************************/
+        if (operationDocs.size() > 0) {
+            //手术记录
+            List<OperationRecordDoc> operationRecordDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationRecordDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationRecordLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("手术时间")))
+                    .collect(Collectors.toList());
+            operationRecordDocs.forEach(x -> getCourseDrugInfo(antibioticDateCourse, x.getStructureMap().get("手术时间"), x.getOperationRecordLabel().getDrugs()
+                    , CatalogueUtil.structureMapJoin(x.getStructureMap(), Lists.newArrayList("手术经过及处理"))));
+            //术后首程
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel().getOperativeFindings() != null && StringUtil.isNotBlank(x.getStructureMap().get("病历日期")))
+                    .collect(Collectors.toList());
+            operationDiscussionDocs.forEach(x -> getCourseDrugInfo(antibioticDateCourse, x.getStructureMap().get("病历日期"), x.getOperationDiscussionLabel().getDrugs()
+                    , CatalogueUtil.structureMapJoin(x.getStructureMap(), Lists.newArrayList("手术简要经过", "术后处理措施"))));
+        }
+        /*********************************************会诊结果单********************************************************/
+        /*if (consultationDocs.size() > 0) {
+            List<ConsultationResultsDoc> consultationResultsDocs = consultationDocs
+                    .stream()
+                    .map(ConsultationDoc::getConsultationResultsDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getConsultationResultLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("会诊日期及时间")))
+                    .collect(Collectors.toList());
+            consultationResultsDocs.forEach(x -> getCourseDrugInfo(antibioticDateCourse, x.getStructureMap().get("会诊日期及时间"), x.getConsultationResultLabel().getDrugs()
+                    , CatalogueUtil.structureMapJoin(x.getStructureMap(), Lists.newArrayList("会诊意见"))));
+        }*/
+        /*********************************************出院小结********************************************************/
+//        if (leaveHospitalDoc != null) {
+//            LeaveHospitalLabel leaveHospitalLabel = leaveHospitalDoc.getLeaveHospitalLabel();
+//            if (inputInfo.getMedicalRecordInfoDoc() != null) {
+//                Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+//                dateStr = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+//                //如果存在出院小结,出院日期为空,存储系统当前时间
+//                if (StringUtil.isBlank(dateStr)) {
+//                    dateStr = DateUtil.formatDateTime(new Date());
+//                }
+//            }
+//            if (leaveHospitalLabel != null && StringUtil.isNotBlank(dateStr)) {
+//                List<Drug> drugs = leaveHospitalLabel.getDrugs();
+//                getCourseDrugInfo(antibioticDateCourse, dateStr, drugs
+//                        , CatalogueUtil.structureMapJoin(leaveHospitalDoc.getStructureMap(), Lists.newArrayList("诊治经过")));
+//            }
+//        }
+        //将病程日期排序
+        antibioticDateCourse.forEach((x, y) -> y.sort(Comparator.naturalOrder()));
+
+        /**
+         * 1.antibioticDate:从医嘱中取   key:抗生素名    value:医嘱中该抗生素所有剂量变化的时间(包括初始使用时间)
+         * 2.antibioticDateWard:从查房记录中取     key:抗生素名    value:病程记录中该抗生素所有没有用量时的查房时间(包括初始使用时间)
+         * 3.医嘱中该抗生素初始使用时间往后两天内,查房记录中出现该抗生素并且该抗生素没有用量,报出该抗生素
+         */
+        StringBuffer sb = new StringBuffer();
+        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();
+            drugKey = removeBracket(drugKey).replaceAll("[^\u4e00-\u9fa5]", "");
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            if (antibioticDateCourse.containsKey(drugKey)) {
+                dateList = ad.getValue();
+                List<String> wardDateStr = antibioticDateCourse.get(drugKey);
+                for (int i = 0; i < dateList.size(); i++) {
+                    start = dateList.get(i);        //抗生素开医嘱时间
+                    Date adStart = DateUtil.dateZeroClear(StringUtil.parseDateTime(start));
+                    for (int j = 0; j < wardDateStr.size(); j++) {
+                        wardStartStr = wardDateStr.get(j);         //查房记录开抗生素时间
+                        wardStartStr = wardStartStr.split("=")[0];
+                        Date wardStart = StringUtil.parseDateTime(wardStartStr);
+                        if ((adStart.before(wardStart) && !CatalogueUtil.compareTime(adStart, wardStart, 48 * 60L))
+                                || (wardStart.before(adStart) && !CatalogueUtil.compareTime(wardStart, adStart, 24 * 60L)) ||
+                                DateUtils.isSameDay(wardStart, adStart)) {
+                            infoAppend(sb, ad.getKey(), start, wardDateStr.get(j).split("=")[1]);
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set("医嘱:" + sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 记录同一抗生素同一天内是否开过多次,用于医嘱中需要处理的抗生素过滤(一天内同一抗生素开过多次的抗生素直接过滤)
+     *
+     * @param docAdvStruct
+     * @param antibioticDateTimes
+     */
+    private void getAntibioticTimes(List<Map<String, String>> docAdvStruct, Map<String, Map<Date, Integer>> antibioticDateTimes) {
+        String drugName;
+        String startDateStr;
+        Date startDate;
+        Map<Date, Integer> antibioticDateTime;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = DateUtil.dateZeroClear(StringUtil.parseDateTime(startDateStr));
+            if (antibioticDateTimes.containsKey(drugName)) {
+                Map<Date, Integer> map = antibioticDateTimes.get(drugName);
+                if (map.containsKey(startDate)) {
+                    map.put(startDate, map.get(startDate) + 1);
+                } else {
+                    map.put(startDate, 0);
+                }
+            } else {
+                antibioticDateTime = Maps.newHashMap();
+                antibioticDateTime.put(startDate, 0);
+                antibioticDateTimes.put(drugName, antibioticDateTime);
+            }
+        }
+    }
+
+    List<String> usageWords = Lists.newArrayList("WB", "wb", "泵", "静滴");
+
+    /**
+     * 收集各模块药品信息
+     *
+     * @param antibioticDateWard 病程中没有用量+用法+频率的抗生素使用所有时间
+     * @param dateStr            记录日期
+     * @param drugs              模型提取出的药品列表
+     */
+    private void getCourseDrugInfo(Map<String, List<String>> antibioticDateWard, String dateStr, List<Drug> drugs, String content) {
+        StringBuffer sb = null;
+        for (Drug drug : drugs) {
+            sb = new StringBuffer();
+            String behindWord = "";
+            String lastBehindWord = "";
+            String keyword = "";
+            String lastKeyword = "";
+            String wardDrug = drug.getName();
+            boolean front = false;
+            boolean latter = false;
+            int position = content.indexOf(wardDrug);
+            int lastPosition = content.lastIndexOf(wardDrug);
+            if (position != -1 && lastPosition != -1) {
+                keyword = content.substring(Math.max(0, position - 10), position);
+                lastKeyword = content.substring(Math.max(0, lastPosition - 10), lastPosition);
+                if (position != lastPosition) {
+                    if (lastKeyword.contains("继续") || lastKeyword.contains("停")) {
+                        continue;
+                    }
+                }
+                if (keyword.contains("继续") || keyword.contains("停")) {
+                    continue;
+                }
+                if (frequencyDispose(keyword) || frequencyDispose(lastKeyword)) {
+                    front = true;
+                }
+
+                behindWord = content.substring(position, Math.min(position + 20, content.toCharArray().length));
+                lastBehindWord = content.substring(lastPosition, Math.min(lastPosition + 20, content.toCharArray().length));
+                if (behindWord != lastBehindWord) {
+                    if (lastBehindWord.contains("阴性")) {
+                        continue;
+                    }
+                }
+                if (behindWord.contains("阴性")) {
+                    continue;
+                }
+                if (frequencyDispose(behindWord) || frequencyDispose(lastBehindWord)) {
+                    latter = true;
+                }
+            }
+            wardDrug = removeBracket(wardDrug);
+            String drugUsageWard = wardDrug;
+            String drugStandardWord = similarityUtil.getDrugStandardWord(wardDrug);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                wardDrug = drugStandardWord;
+            }
+            if (drug.getConsumption() == null) {
+                if (StringUtil.isBlank(behindWord)) {
+                    concatInfo(dateStr, sb, "用量");
+                } else {
+                    if (!hasDigit(behindWord) && !hasDigit(lastBehindWord)) {
+                        concatInfo(dateStr, sb, "用量");
+                    }
+                }
+            }
+
+            int index = content.indexOf(drugUsageWard);
+            String drugContent = content.substring(Math.max(0, index));
+            boolean isUsage = true;
+            if (drug.getUsageWardRound() == null) {
+                for (String word : usageWords) {
+                    if (drugContent.contains(word)) {
+                        isUsage = false;
+                        break;
+                    }
+                }
+                if (isUsage) {
+                    concatInfo(dateStr, sb, "用法");
+                }
+            }
+            if (drug.getFrequency() == null) {
+                if (!front && !latter) {
+                    concatInfo(dateStr, sb, "频率");
+                }
+            }
+            if (sb.toString().length() > 0) {
+                if (antibioticDateWard.containsKey(wardDrug)) {
+                    antibioticDateWard.get(wardDrug).add(sb.toString());
+                } else {
+                    antibioticDateWard.put(wardDrug, Lists.newArrayList(sb.toString()));
+                }
+            } else {
+                if (antibioticDateWard.containsKey(wardDrug)) {
+                    for (String wardDate : antibioticDateWard.get(wardDrug)) {
+                        wardDate = wardDate.substring(0, wardDate.indexOf("="));
+                        if (wardDate.equals(dateStr)) {
+                            antibioticDateWard.remove(wardDrug);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 判断一个字符串是否含有数字
+     *
+     * @param content
+     */
+    public boolean hasDigit(String content) {
+        boolean flag = false;
+        Pattern p = Pattern.compile(".*\\d+.*");
+        Matcher m = p.matcher(content);
+        if (m.matches()) {
+            flag = true;
+        }
+        return flag;
+    }
+
+    /**
+     * 增加频率的处理
+     *
+     * @param content
+     */
+    public boolean frequencyDispose(String content) {
+        if (StringUtil.isNotBlank(content) &&
+                (content.contains("术前") || content.contains("术后") || content.contains("围手术期"))) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * 拼接抗生素缺失信息
+     *
+     * @param dateStr
+     * @param sb
+     */
+    private void concatInfo(String dateStr, StringBuffer sb, String missType) {
+        if (sb.toString().contains("=")) {
+            sb.append(",").append(missType);
+        } else {
+            sb.append(dateStr).append("=").append(missType);
+        }
+    }
+
+    /**
+     * 收集抗生素各种信息
+     *
+     * @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("THR03077:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (v > 100) {
+            v = v / 1000;
+        }
+        if (!antibioticValue.containsKey(drugName)) {
+            antibioticValue.put(drugName, Lists.newArrayList(v));
+            antibioticDate.put(drugName, Lists.newArrayList(startDateStr));
+            antibioticStatus.put(drugName, 0);
+        } else {
+            //1.如果抗生素剂量有变化,则记录该抗生素开始时间
+            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);
+                return;
+            }
+            //2.如果抗生素剂量两次开启的时间间隔相差3天,也记录该抗生素开始时间
+            List<String> currentAntibioticDate = antibioticDate.get(drugName);
+            if (currentAntibioticDate.size() > 0) {
+                String lastDate = currentAntibioticDate.get(currentAntibioticDate.size() - 1);
+                if (CatalogueUtil.compareTime(StringUtil.parseDateTime(lastDate), StringUtil.parseDateTime(startDateStr), 72 * 60L)) {
+                    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;
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date, String missType) {
+        sb.append(drugKey).append("(").append(DateUtil.formatDate(StringUtil.parseDateTime(date)))
+                .append(",").append(missType).append(")").append("、");
+    }
+
+    private static final String[] KSS = {
+            "万古霉素",
+            "两性霉素B",
+            "亚胺培南西司他丁",
+            "伊曲康唑",
+            "伏立康唑",
+            "依替米星氯化钠",
+            "克拉霉素",
+            "克林霉素",
+            "利奈唑胺",
+            "利奈唑胺葡萄糖",
+            "利福昔明",
+            "制霉菌素",
+            "卡泊芬净",
+            "厄他培南",
+            "吗啉硝唑氯化钠",
+            "呋喃唑酮",
+            "哌拉西林他唑巴坦",
+            "复方磺胺甲噁唑",
+            "多粘菌素B",
+            "多西环素",
+            "夫西地酸",
+            "头孢丙烯",
+            "头孢他啶",
+            "头孢他啶阿维巴坦",
+            "头孢他美酯",
+            "头孢克洛",
+            "头孢克肟",
+            "头孢吡肟",
+            "头孢呋辛",
+            "头孢哌酮舒巴坦",
+            "头孢唑林",
+            "头孢噻肟",
+            "头孢地嗪",
+            "头孢地尼",
+            "头孢拉定",
+            "头孢曲松",
+            "头孢替安",
+            "头孢美唑",
+            "头孢西丁",
+            "奥硝唑",
+            "妥布霉素",
+            "妥布霉素地塞米松",
+            "左氧氟沙星",
+            "左氧氟沙星氯化钠",
+            "庆大霉素",
+            "异帕米星",
+            "拉氧头孢",
+            "替加环素",
+            "替硝唑",
+            "替考拉宁",
+            "比阿培南",
+            "氟康唑",
+            "氟康唑氯化钠",
+            "氟胞嘧啶",
+            "氨曲南",
+            "氨苄西林",
+            "泊沙康唑",
+            "特比萘芬",
+            "甲硝唑",
+            "甲硝唑氯化钠",
+            "磷霉素",
+            "磷霉素氨丁三醇",
+            "米卡芬净",
+            "米诺环素",
+            "红霉素",
+            "美罗培南",
+            "苄星青霉素",
+            "莫西沙星",
+            "莫西沙星氯化钠",
+            "达托霉素",
+            "阿奇霉素",
+            "阿奇霉素枸橼酸二氢钠",
+            "阿洛西林",
+            "阿米卡星",
+            "阿莫西林",
+            "阿莫西林克拉维酸",
+            "青霉素"
+    };
+
+    private static final List<String> filterKey = Lists.newArrayList("ACF", "ID", "IG", "IM", "IP", "IV",
+            "关节腔注射", "宫颈注射", "皮下注射", "皮下注射(儿童)", "皮下注射(免费)", "皮下注射(成人)", "皮内", "皮内注射",
+            "结膜下注射", "肌注", "肌肉注射(儿童)", "肌肉注射(公卫专用)", "肌肉注射(成人)", "胸腔注射", "腹腔内注射", "腹腔注射",
+            "静滴(儿童)", "静滴(成人)", "静脉注射", "静脉注射(儿童)", "静脉注射(免费)", "静脉注射(成人)", "静脉注射(泵)",
+            "静脉滴注", "静脉滴注(泵)", "鞘内注射", "微泵");
+
+}

+ 434 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03079.java

@@ -0,0 +1,434 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.LeaveHospitalDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.consultation.ConsultationDoc;
+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.DrugLabel;
+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.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-09-18 14:23
+ * @desc 病程记录激素剂量与医嘱不一致
+ **/
+@Component
+public class THR03079 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();
+        FirstCourseRecordDoc firstCourseRecordDoc = inputInfo.getFirstCourseRecordDoc();
+        List<ConsultationDoc> consultationDocs = inputInfo.getConsultationDocs();
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+        if (doctorAdviceDocs.size() == 0) {
+            return;
+        }
+
+        Map<Date, String> extData = null;
+        if (outputInfo.getResult().get("THR02986") != null) {
+            extData = (Map<Date, String>) outputInfo.getResult().get("THR02986").get("extData");
+        }
+
+        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("医嘱单次剂量")))
+//                .filter(x -> StringUtil.isNotBlank(x.get("医嘱状态判别")) && !x.get("医嘱状态判别").contains("已停止"))
+                .collect(Collectors.toList());
+        docAdvStruct.removeIf(x -> StringUtil.isNotBlank(x.get("给药方式")) && !filterKey.contains(x.get("给药方式")));
+
+        //激素及开医嘱时间 <激素名,<激素用量,[激素使用时间...]>>
+        Map<String, Map<String, List<Double>>> antibioticInfo = Maps.newLinkedHashMap();
+        Map<String, Map<Date, Integer>> antibioticDateTimes = Maps.newHashMap();
+        //记录同一激素同一天内是否开过多次,用于医嘱中需要处理的激素过滤(一天内同一激素开过多次的激素直接过滤)
+        getAntibioticTimes(docAdvStruct, antibioticDateTimes);
+        String drugName = null, value = null, startDateStr = null;
+        Date startDate = null;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            value = structMap.get("医嘱单次剂量");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = StringUtil.parseDateTime(startDateStr);
+            drugName = removeBracket(drugName).replaceAll("[^\u4e00-\u9fa5]", "");
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugName = drugStandardWord;
+            }
+            if (extData != null && extData.containsKey(startDate) && extData.get(startDate).equals(drugName)) {
+                continue;   //THR02986  医嘱有激素使用病程无记录,规则中没报未记录的激素继续走这条规则,报未记录的激素过滤
+            }
+            if (antibioticDateTimes.get(drugName) != null) {
+                if (antibioticDateTimes.get(drugName).get(startDate) != null && antibioticDateTimes.get(drugName).get(startDate) > 0) {
+                    continue;   //一天内同一激素开过多次的激素直接过滤
+                }
+            }
+
+            if (drugName.contains("甲泼尼龙") || drugName.contains("泼尼松") || drugName.contains("地塞米松") || drugName.contains("可的松")) {
+                collectAntibioticInfo(antibioticInfo, structMap.get("医嘱项目名称"), value, startDateStr);
+            }
+        }
+
+        //激素及开医嘱时间 <激素名,<激素用量,[激素使用时间...]>>
+        Map<String, Map<String, List<Double>>> antibioticWardInfo = Maps.newLinkedHashMap();
+        String dateStr = null;
+        Map<String, Date> mapInfo = Maps.newLinkedHashMap();
+        /*********************************************首程治疗计划********************************************************/
+        if (firstCourseRecordDoc != null) {
+            getInfo(mapInfo, firstCourseRecordDoc.getStructureMap(), "首次病程录", "病历日期", "治疗计划");
+            DrugLabel drugLabel = firstCourseRecordDoc.getDrugLabel();
+            dateStr = firstCourseRecordDoc.getStructureMap().get("病历日期");
+            if (drugLabel != null && StringUtil.isNotBlank(dateStr)) {
+                List<Drug> drugs = drugLabel.getDrugs();
+                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
+            }
+        }
+        /*********************************************查房记录********************************************************/
+        if (threeLevelWardDocs.size() > 0) {
+            List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+            List<ThreeLevelWardDoc> wardDocs = allDoctorWradDocs
+                    .stream()
+                    .filter(x -> StringUtil.isNotBlank(x.getStructureMap().get("查房日期")) && x.getThreeLevelWardLabel().size() > 0)
+                    .collect(Collectors.toList());
+            wardDocs.forEach(x -> getInfo(mapInfo, x.getStructureMap(), "查房记录", "查房日期", "病情记录", "治疗计划和措施"));
+            wardDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getThreeLevelWardLabel().get(x.getThreeLevelWardLabel().size() - 1).getDrugs(), x.getStructureMap().get("查房日期")));
+        }
+        /**********************************************手术记录、术后首程************************************************/
+        if (operationDocs.size() > 0) {
+            //手术记录
+            List<OperationRecordDoc> operationRecordDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationRecordDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationRecordLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("手术时间")))
+                    .collect(Collectors.toList());
+            operationRecordDocs.forEach(x -> getInfo(mapInfo, x.getStructureMap(), "手术记录", "手术时间", "手术经过及处理"));
+            operationRecordDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getOperationRecordLabel().getDrugs(), x.getStructureMap().get("手术时间")));
+            //术后首程
+            List<OperationDiscussionDoc> operationDiscussionDocs = operationDocs
+                    .stream()
+                    .map(OperationDoc::getOperationDiscussionDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getOperationDiscussionLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("病历日期")))
+                    .collect(Collectors.toList());
+            operationDiscussionDocs.forEach(x -> getInfo(mapInfo, x.getStructureMap(), "术后首程", "病历日期", "手术简要经过", "术后处理措施"));
+            operationDiscussionDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getOperationDiscussionLabel().getDrugs(), x.getStructureMap().get("病历日期")));
+        }
+        /*********************************************会诊结果单********************************************************/
+        /*if (consultationDocs.size() > 0) {
+            List<ConsultationResultsDoc> consultationResultsDocs = consultationDocs
+                    .stream()
+                    .map(ConsultationDoc::getConsultationResultsDoc)
+                    .filter(Objects::nonNull)
+                    .filter(x -> x.getConsultationResultLabel() != null && StringUtil.isNotBlank(x.getStructureMap().get("会诊日期及时间")))
+                    .collect(Collectors.toList());
+            consultationResultsDocs.forEach(x -> getCourseDrugInfo(antibioticWardInfo, x.getConsultationResultLabel().getDrugs(), x.getStructureMap().get("会诊日期及时间")));
+        }*/
+        /*********************************************出院小结********************************************************/
+        if (leaveHospitalDoc != null) {
+            if (inputInfo.getMedicalRecordInfoDoc() != null) {
+                Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+                dateStr = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+                if (StringUtil.isBlank(dateStr)) {
+                    dateStr = DateUtil.formatDateTime(new Date());
+                }
+                //如果存在出院小结,出院日期为空,存储系统当前时间
+            }
+            if (leaveHospitalDoc.getLeaveHospitalLabel() != null && StringUtil.isNotBlank(dateStr)) {
+                getInfo(mapInfo, leaveHospitalDoc.getStructureMap(), "出院小结", "出院时间", "诊治经过");
+                List<Drug> drugs = leaveHospitalDoc.getLeaveHospitalLabel().getDrugs();
+                getCourseDrugInfo(antibioticWardInfo, drugs, dateStr);
+            }
+        }
+
+        /**
+         * 1.医嘱中开了激素,查房记录中没有该激素,则医嘱中该激素出现过的所有时间都会提示出来
+         * 2.医嘱中开了激素,查房记录中有该激素:
+         *      2.1 医嘱中该激素某使用量(如50),查房记录中没有该使用量(如只有100),则医嘱中该激素使用量出现过的所有时间都会提示出来
+         *      2.2 医嘱中该激素某使用量(如50),查房记录中也有该使用量,对比这两个时间,若医嘱时间两天内的查房记录没有该使用量,则该医嘱时间会提示出来
+         */
+        StringBuffer sb = new StringBuffer();
+        String drugKey = null;
+        for (Map.Entry<String, Map<String, List<Double>>> ai : antibioticInfo.entrySet()) {
+            drugKey = ai.getKey();
+            drugKey = removeBracket(drugKey).replaceAll("[^\u4e00-\u9fa5]", "");
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugKey);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugKey = drugStandardWord;
+            }
+            if (antibioticWardInfo.containsKey(drugKey)) {
+                Map<String, List<Double>> adDateValue = ai.getValue();
+                Map<String, List<Double>> wardDateValue = antibioticWardInfo.get(drugKey);
+                for (Map.Entry<String, List<Double>> adMap : adDateValue.entrySet()) {
+                    boolean match = false;
+                    String adDateStr = adMap.getKey();
+                    Date adDate = StringUtil.parseDateTime(adDateStr);
+                    List<Double> adUsage = adMap.getValue();
+                    for (Map.Entry<String, List<Double>> wdvMap : wardDateValue.entrySet()) {
+                        String wardDateStr = wdvMap.getKey();
+                        Date wardDate = StringUtil.parseDateTime(wardDateStr);
+                        List<Double> wardUsage = wdvMap.getValue();
+                        adDate = DateUtil.dateZeroClear(adDate);
+                        if (adDate.before(wardDate) && !CatalogueUtil.compareTime(adDate, wardDate, 48 * 60L)) {
+                            if (wardUsage.toString().equals(adUsage.toString())) {
+                                match = true;
+                            }
+                        }
+                        if (wardDate.before(adDate) && !CatalogueUtil.compareTime(wardDate, adDate, 24 * 60L)) {
+                            boolean isWard = false;
+                            if (wardUsage.toString().equals(adUsage.toString())) {
+                                isWard = true;
+                            }
+                            match = isWard;
+                        }
+                    }
+                    adDateStr = DateUtil.formatDate(adDate);
+                    if (!match && !sb.toString().contains(drugKey + "(" + adDateStr + ")")) {
+                        List<Double> drugDosage = isContinueTreat(ai.getKey(), adDate, mapInfo, wardDateValue);
+                        //去*号
+                        drugKey = ai.getKey().replace("*", "");
+                        if (drugDosage == null) {
+                            infoAppend(sb, drugKey, adDateStr);
+                        } else {
+                            drugDosage.removeAll(adUsage);//比如adUsage有1.0、2.0,wardUsage中有2.0、3.0,removeAll之后wardUsage只剩3.0
+                            if (drugDosage.size() != 0) {
+                                infoAppend(sb, drugKey, adDateStr);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set("医嘱:" + sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 记录病程记录中是否有继续治疗,治疗同前等
+     *
+     * @param drug
+     * @param adDate
+     * @param mapInfo
+     * @param wardDateMaps
+     */
+    private List<Double> isContinueTreat(String drug, Date adDate, Map<String, Date> mapInfo, Map<String, List<Double>> wardDateMaps) {
+        for (Map.Entry<String, Date> map : mapInfo.entrySet()) {
+            String content = map.getKey();
+            Date dateValue = map.getValue();
+            //开医嘱时间起,昨天今天明天记录内查找药
+            if ((adDate.before(dateValue) && !CatalogueUtil.compareTime(adDate, dateValue, 48 * 60L))
+                    || (dateValue.before(adDate) && !CatalogueUtil.compareTime(dateValue, adDate, 24 * 60L))) {
+                String standardDrug = null;
+                drug = drug.replaceAll("[^\\u4e00-\\u9fa5]", "");
+                standardDrug = similarityUtil.getDrugStandardWord(drug);
+                if (StringUtil.isBlank(drug)) {
+                    continue;
+                }
+                if (!content.contains(drug) && (StringUtil.isNotBlank(content) && StringUtil.isNotBlank(standardDrug) && !content.contains(standardDrug)) &&
+                        (regexFind(content, "继续", "治疗") || regexFind(content, "维持", "治疗") || regexFind(content, "继续", "抗感染") ||
+                                regexFind(content, "治疗", "同前") || regexFind(content, "治疗同前"))) {
+                    List<Double> drugDosage = null;
+                    for (Map.Entry<String, List<Double>> wdvMap : wardDateMaps.entrySet()) {
+                        String wardDateStr = wdvMap.getKey();
+                        Date wardDate = StringUtil.parseDateTime(wardDateStr);
+                        if (wardDate.before(dateValue)) {
+                            drugDosage = wdvMap.getValue();
+                        }
+                    }
+                    return drugDosage;
+                }
+            }
+        }
+        return null;
+    }
+
+    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 docAdvStruct
+     * @param antibioticDateTimes
+     */
+    private void getAntibioticTimes(List<Map<String, String>> docAdvStruct, Map<String, Map<Date, Integer>> antibioticDateTimes) {
+        String drugName;
+        String startDateStr;
+        Date startDate;
+        Map<Date, Integer> antibioticDateTime;
+        for (Map<String, String> structMap : docAdvStruct) {
+            drugName = structMap.get("医嘱项目名称");
+            startDateStr = structMap.get("医嘱开始时间");
+            startDate = StringUtil.parseDateTime(startDateStr);
+            String drugStandardWord = similarityUtil.getDrugStandardWord(drugName);
+            if (StringUtil.isNotBlank(drugStandardWord)) {
+                drugName = drugStandardWord;
+            }
+            if (antibioticDateTimes.containsKey(drugName)) {
+                Map<Date, Integer> map = antibioticDateTimes.get(drugName);
+                if (map.containsKey(startDate)) {
+                    map.put(startDate, map.get(startDate) + 1);
+                } else {
+                    map.put(startDate, 0);
+                }
+            } else {
+                antibioticDateTime = Maps.newHashMap();
+                antibioticDateTime.put(startDate, 0);
+                antibioticDateTimes.put(drugName, antibioticDateTime);
+            }
+        }
+    }
+
+    private void getCourseDrugInfo(Map<String, Map<String, List<Double>>> antibioticWardInfo, List<Drug> drugs, String dateStr) {
+        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(antibioticWardInfo, wardDrug, consumption, dateStr);
+            }
+        }
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param drugKey
+     * @param date
+     */
+    private void infoAppend(StringBuffer sb, String drugKey, String date) {
+        sb.append(drugKey).append("(").append(date).append(")").append("_");
+    }
+
+    /**
+     * 获取各模块信息<入院记录、首次病程录、手术记录、术后首程、会诊结果单、查房记录、出院小结>
+     *
+     * @param info
+     * @param structureMap
+     * @param modelType
+     * @param dateKey
+     * @param contentKey
+     */
+    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);
+            }
+        }
+    }
+
+    /**
+     * 收集激素各种信息
+     *
+     * @param antibioticInfo 激素使用量及所有时间
+     * @param drugName       激素名称
+     * @param value          激素用量
+     * @param startDateStr   激素使用时间(医嘱开始时间或查房时间)
+     */
+    private void collectAntibioticInfo(Map<String, Map<String, List<Double>>> antibioticInfo, String drugName, String value, String startDateStr) {
+        Map<String, List<Double>> antibioticValueList = null;
+        double v = -1;
+        try {
+            v = Double.parseDouble(getNumber(value));
+        } catch (Exception e) {
+            System.out.println("THR03076:       " + drugName + ":" + value + "解析异常");
+        }
+        if (v < 0) {
+            return;
+        }
+        if (v > 100) {
+            v = v / 1000;
+        }
+        if (!antibioticInfo.containsKey(drugName)) {
+            //存该激素使用第1个值
+            antibioticValueList = Maps.newLinkedHashMap();
+            antibioticValueList.put(startDateStr, Lists.newArrayList(v));
+            antibioticInfo.put(drugName, antibioticValueList);
+        } else {
+            antibioticValueList = antibioticInfo.get(drugName);
+            //存该激素使用时间时第n个量
+            if (antibioticValueList.containsKey(startDateStr)) {
+                antibioticValueList.get(startDateStr).add(v);
+            } else {
+                //存该激素使用时间时第1个量
+                antibioticValueList.put(startDateStr, Lists.newArrayList(v));
+            }
+        }
+    }
+
+    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;
+    }
+
+    private static final List<String> filterKey = Lists.newArrayList("口服", "静脉滴注", "静脉注射(泵)", "静脉注射", "皮下注射", "冲服",
+            "肌注", "关节腔注射", "饭后口服", "饭前口服", "饭中口服", "嚼服", "局部注射", "术中宫体注射",
+            "含服", "饭后冲服", "皮内注射", "饭后嚼服", "饭前嚼服", "泡服", "动脉注射",
+            "IG", "PO", "SC", "IM", "IV", "IP", "JB", "GT", "OWH", "ACI", "XX");
+
+}

+ 91 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR03090.java

@@ -0,0 +1,91 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.DoctorAdviceDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * @ClassName : THR03090
+ * @Description : 手术患者无术前主刀医师查房记录
+ * @Author : wsy
+ * @Date: 2021-01-11 10:39
+ */
+@Component
+public class THR03090 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        long cou = inputInfo.getOperationDocs().stream().map(OperationDoc::getOperationRecordDoc).filter(Objects::nonNull).count();
+        if (cou == 0) {
+            return;
+        }
+        if (threeLevelWardDocs.size() == 0 && cou > 0) {
+            status.set("-1");
+            return;
+        }
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs == null || operationDocs.size() == 0) {
+            return;
+        }
+        String operationStartDate = "";
+        if (operationDocs.get(operationDocs.size() - 1).getOperationRecordDoc() != null) {
+            Map<String, String> operationDocStructureMap = operationDocs.get(operationDocs.size() - 1).getOperationRecordDoc().getStructureMap();
+            operationStartDate = operationDocStructureMap.get("手术日期");
+            if (StringUtil.isNotBlank(operationStartDate)) {
+                if (operationStartDate.contains("结束")) {
+                    operationStartDate = operationStartDate.substring(0, operationStartDate.indexOf("结束")).replace("开始:", "");
+                }
+                if (operationStartDate.contains("年月日")) {
+                    operationStartDate = DateUtil.nowString();
+                }
+            }
+
+        }
+
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc threeLevelWard : allDoctorWradDocs) {
+            Map<String, String> structureMap = threeLevelWard.getStructureMap();
+            String makeTitle = structureMap.get("查房标题");
+            String writTitle = structureMap.get("文书标题");
+            String makeDate = structureMap.get("查房日期");
+            if (StringUtil.isNotBlank(makeDate) && StringUtil.parseDateTime(makeDate).before(StringUtil.parseDateTime(operationStartDate))) {
+                if (((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("主刀")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("主刀"))
+                        || ((StringUtil.isNotBlank(makeTitle) && makeTitle.contains("术前")) || (StringUtil.isNotBlank(writTitle) && writTitle.contains("术前"))))) {
+                    return;
+                }
+            }
+        }
+
+        //判断医嘱里有无手术
+        List<DoctorAdviceDoc> doctorAdviceDocs = inputInfo.getDoctorAdviceDocs();
+        if (doctorAdviceDocs.size() == 0) {
+            return;
+        }
+        for (DoctorAdviceDoc dad : doctorAdviceDocs) {
+            String name = dad.getStructureMap().get("医嘱项目名称");
+            if (name.contains("非手术") || name.contains("手术室") || (name.contains("手术") && name.contains("取消")) || (name.contains("暂停") && name.contains("手术")) || name.contains("静脉穿刺置管术") || name.startsWith("停") || name.contains("前一次")
+                    || name.contains("特殊病人手术使用一次性卫生材料") || name.contains("人免疫缺陷病毒抗体检测免费")) {
+                continue;
+            }
+            if (name.contains("手术")) {
+                status.set("-1");
+                return;
+            }
+        }
+
+        if (cou > 0) {
+            status.set("-1");
+            return;
+        }
+    }
+}

+ 136 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0434.java

@@ -0,0 +1,136 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DeathRecordDoc;
+import com.lantone.qc.pub.model.doc.SeriouslyIllNoticeDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR0434
+ * @Description : 危重患者无上级医师(副高及以上)查房记录
+ * @Author : Mark
+ * @Date: 2020-04-05 16:10
+ */
+@Component
+public class THR0434 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //没有死亡记录并且没有离院记录,直接返回
+        if (inputInfo.getDeathRecordDoc() == null && inputInfo.getLeaveHospitalDoc() == null) {
+            return;
+        }
+        //没有病重通知书或者查房记录,直接返回
+        if (ListUtil.isEmpty(inputInfo.getSeriouslyIllNoticeDocs()) || ListUtil.isEmpty(inputInfo.getThreeLevelWardDocs())) {
+            return;
+        }
+        //死亡记录不为空
+        if (inputInfo.getDeathRecordDoc() != null) {
+            DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+            Map<String, String> deathRecordStructureMap = deathRecordDoc.getStructureMap();
+            String deathTime = deathRecordStructureMap.get("死亡时间");
+            if (StringUtil.isNotBlank(deathTime)) {
+                Date deathDate = StringUtil.parseDateTime(deathTime);
+                if (deathDate != null) {
+                    dealWithRescueRecord(inputInfo, deathDate);
+                }
+            }
+        }
+        //离院记录不为空
+        if (inputInfo.getLeaveHospitalDoc() != null && inputInfo.getMedicalRecordInfoDoc() != null) {
+            Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+            String leaveHospitalTime = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+            if (StringUtil.isNotBlank(leaveHospitalTime)) {
+                Date leaveHospitalDate = StringUtil.parseDateTime(leaveHospitalTime);
+                if (leaveHospitalDate != null) {
+                    dealWithRescueRecord(inputInfo, leaveHospitalDate);
+                }
+            }
+        }
+    }
+
+    private void dealWithRescueRecord(InputInfo inputInfo, Date timeOfComparison) {
+        List<SeriouslyIllNoticeDoc> seriouslyIllNoticeDocs = inputInfo.getSeriouslyIllNoticeDocs(); //病危通知书
+        Map<Date, Map<String, String>> dateRecord = new TreeMap<>(new Comparator<Date>() {
+            @Override
+            public int compare(Date o1, Date o2) {
+                // 降序排列
+                return o1.compareTo(o2);
+            }
+        });//按时间key排序,存放病危通知书、查房记录
+        String recordTime = "", title = "";
+        for (SeriouslyIllNoticeDoc seriouslyIllNoticeDoc : seriouslyIllNoticeDocs) {
+            Map<String, String> seriouslyIllNoticeDocMap = seriouslyIllNoticeDoc.getStructureMap();
+            recordTime = seriouslyIllNoticeDocMap.get("签名时间");
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null) {
+                continue;
+            }
+            dateRecord.put(recordDate, seriouslyIllNoticeDocMap);
+        }
+        dateRecord = extractSeriouslyIll(dateRecord);//24小时内记录只取第一条
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();//查房记录
+        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+            Map<String, String> rescueStructureMap = threeLevelWardDoc.getStructureMap();
+            recordTime = rescueStructureMap.get("查房日期");
+            title = CatalogueUtil.subTitle(rescueStructureMap.get("查房标题"));
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null || StringUtil.isBlank(title) || !title.contains(Content.director)) {//只存副高以上查房记录
+                continue;
+            }
+            dateRecord.put(recordDate, rescueStructureMap);
+        }
+
+        List<Map<String, String>> dateRecordList = new ArrayList<>(dateRecord.values());
+        //如果最后一条是病危通知书,则之后肯定没有查房记录
+        if (dateRecordList.get(dateRecordList.size() - 1).containsKey("病程记录名称")) {
+            status.set("-1");
+            return;
+        }
+        for (int i = 0; i < dateRecordList.size(); i++) {
+            if (i != dateRecordList.size() - 1) {
+                //当前为病危通知书
+                if (!dateRecordList.get(i).containsKey("签名时间")) {
+                    continue;
+                }
+                //病危通知书下一条不是查房记录则报错
+                if (!dateRecordList.get(i + 1).containsKey("查房日期")) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * 抽取24小时内第一条记录
+     *
+     * @param dateRecord
+     */
+    private Map<Date, Map<String, String>> extractSeriouslyIll(Map<Date, Map<String, String>> dateRecord) {
+        Map<Date, Map<String, String>> record = new TreeMap<>(new Comparator<Date>() {
+            @Override
+            public int compare(Date o1, Date o2) {
+                // 降序排列
+                return o1.compareTo(o2);
+            }
+        });
+        Date lastDate = null;
+        for (Map.Entry<Date, Map<String, String>> dateRecordEntry : dateRecord.entrySet()) {
+            Date difficultCaseDiscussDate = dateRecordEntry.getKey();
+            if (lastDate == null || CatalogueUtil.compareTime(lastDate, difficultCaseDiscussDate, (long) (24 * 60))) {
+                lastDate = difficultCaseDiscussDate;
+                record.put(difficultCaseDiscussDate, dateRecordEntry.getValue());
+            }
+        }
+        return record;
+    }
+}

+ 136 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0435.java

@@ -0,0 +1,136 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DeathRecordDoc;
+import com.lantone.qc.pub.model.doc.DifficultCaseDiscussDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR0435
+ * @Description : 疑难患者无上级医师(副高及以上)查房记录
+ * @Author : 胡敬
+ * @Date: 2020-03-25 10:21
+ */
+@Component
+public class THR0435 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //没有死亡记录并且没有离院记录,直接返回
+        if (inputInfo.getDeathRecordDoc() == null && inputInfo.getLeaveHospitalDoc() == null) {
+            return;
+        }
+        //没有疑难病例讨论记录或者查房记录,直接返回
+        if (ListUtil.isEmpty(inputInfo.getDifficultCaseDiscussDocs()) || ListUtil.isEmpty(inputInfo.getThreeLevelWardDocs())) {
+            return;
+        }
+        //死亡记录不为空
+        if (inputInfo.getDeathRecordDoc() != null) {
+            DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+            Map<String, String> deathRecordStructureMap = deathRecordDoc.getStructureMap();
+            String deathTime = deathRecordStructureMap.get("死亡时间");
+            if (StringUtil.isNotBlank(deathTime)) {
+                Date deathDate = StringUtil.parseDateTime(deathTime);
+                if (deathDate != null) {
+                    dealWithRescueRecord(inputInfo, deathDate);
+                }
+            }
+        }
+        //离院记录不为空
+        if (inputInfo.getLeaveHospitalDoc() != null && inputInfo.getMedicalRecordInfoDoc() != null) {
+            Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+            String leaveHospitalTime = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+            if (StringUtil.isNotBlank(leaveHospitalTime)) {
+                Date leaveHospitalDate = StringUtil.parseDateTime(leaveHospitalTime);
+                if (leaveHospitalDate != null) {
+                    dealWithRescueRecord(inputInfo, leaveHospitalDate);
+                }
+            }
+        }
+    }
+
+    private void dealWithRescueRecord(InputInfo inputInfo, Date timeOfComparison) {
+        List<DifficultCaseDiscussDoc> difficultCaseDiscussDocs = inputInfo.getDifficultCaseDiscussDocs(); //疑难病例讨论记录
+        Map<Date, Map<String, String>> dateRecord = new TreeMap<>(new Comparator<Date>() {
+            @Override
+            public int compare(Date o1, Date o2) {
+                // 降序排列
+                return o1.compareTo(o2);
+            }
+        });//按时间key排序,存放疑难病例讨论记录、查房记录
+        String recordTime = "", title = "";
+        for (DifficultCaseDiscussDoc difficultCaseDiscussDoc : difficultCaseDiscussDocs) {
+            Map<String, String> difficultCaseDiscussStructureMap = difficultCaseDiscussDoc.getStructureMap();
+            recordTime = difficultCaseDiscussStructureMap.get("讨论时间");
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null) {
+                continue;
+            }
+            dateRecord.put(recordDate, difficultCaseDiscussStructureMap);
+        }
+        dateRecord = extractDifficult(dateRecord);//24小时内记录只取第一条
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();//查房记录
+        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+            Map<String, String> rescueStructureMap = threeLevelWardDoc.getStructureMap();
+            recordTime = rescueStructureMap.get("查房日期");
+            title = CatalogueUtil.subTitle(rescueStructureMap.get("查房标题"));
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null || StringUtil.isBlank(title) || !title.contains(Content.director)) {//只存副高以上查房记录
+                continue;
+            }
+            dateRecord.put(recordDate, rescueStructureMap);
+        }
+
+        List<Map<String, String>> dateRecordList = new ArrayList<>(dateRecord.values());
+        //如果最后一条是疑难病例讨论记录,则之后肯定没有查房记录
+        if (dateRecordList.get(dateRecordList.size() - 1).containsKey("抢救时间")) {
+            status.set("-1");
+            return;
+        }
+        for (int i = 0; i < dateRecordList.size(); i++) {
+            if (i != dateRecordList.size() - 1) {
+                //当前为疑难病例讨论记录
+                if (!dateRecordList.get(i).containsKey("讨论时间")) {
+                    continue;
+                }
+                //疑难病例讨论记录下一条不是查房记录则报错
+                if (!dateRecordList.get(i + 1).containsKey("查房日期")) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * 抽取24小时内第一条记录
+     *
+     * @param dateRecord
+     */
+    private Map<Date, Map<String, String>> extractDifficult(Map<Date, Map<String, String>> dateRecord) {
+        Map<Date, Map<String, String>> record = new TreeMap<>(new Comparator<Date>() {
+            @Override
+            public int compare(Date o1, Date o2) {
+                // 降序排列
+                return o1.compareTo(o2);
+            }
+        });
+        Date lastDate = null;
+        for (Map.Entry<Date, Map<String, String>> dateRecordEntry : dateRecord.entrySet()) {
+            Date difficultCaseDiscussDate = dateRecordEntry.getKey();
+            if (lastDate == null || CatalogueUtil.compareTime(lastDate, difficultCaseDiscussDate, (long) (24 * 60))) {
+                lastDate = difficultCaseDiscussDate;
+                record.put(difficultCaseDiscussDate, dateRecordEntry.getValue());
+            }
+        }
+        return record;
+    }
+}

+ 142 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0436.java

@@ -0,0 +1,142 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.DeathRecordDoc;
+import com.lantone.qc.pub.model.doc.RescueDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR0436
+ * @Description : 抢救患者无上级医师(副高及以上)查房记录
+ * @Author : 胡敬
+ * @Date: 2020-03-24 19:35
+ */
+@Component
+public class THR0436 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        //没有死亡记录并且没有离院记录,直接返回
+        if (inputInfo.getDeathRecordDoc() == null && inputInfo.getLeaveHospitalDoc() == null) {
+            return;
+        }
+        //没有抢救记录或者查房记录,直接返回
+        if (ListUtil.isEmpty(inputInfo.getRescueDocs()) || ListUtil.isEmpty(inputInfo.getThreeLevelWardDocs())) {
+            return;
+        }
+        //死亡记录不为空
+        if (inputInfo.getDeathRecordDoc() != null) {
+            DeathRecordDoc deathRecordDoc = inputInfo.getDeathRecordDoc();
+            Map<String, String> deathRecordStructureMap = deathRecordDoc.getStructureMap();
+            String deathTime = deathRecordStructureMap.get("死亡时间");
+            if (StringUtil.isNotBlank(deathTime)) {
+                Date deathDate = StringUtil.parseDateTime(deathTime);
+                if (deathDate != null) {
+                    dealWithRescueRecord(inputInfo, deathDate);
+                }
+            }
+        }
+        //离院记录不为空
+        if (inputInfo.getLeaveHospitalDoc() != null && inputInfo.getMedicalRecordInfoDoc() != null) {
+            Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+            String leaveHospitalTime = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+            if (StringUtil.isNotBlank(leaveHospitalTime)) {
+                Date leaveHospitalDate = StringUtil.parseDateTime(leaveHospitalTime);
+                if (leaveHospitalDate != null) {
+                    dealWithRescueRecord(inputInfo, leaveHospitalDate);
+                }
+            }
+        }
+    }
+
+    private void dealWithRescueRecord(InputInfo inputInfo, Date timeOfComparison) {
+        List<RescueDoc> rescueDocs = inputInfo.getRescueDocs(); //抢救记录
+        Map<Date, Map<String, String>> dateRecord = new TreeMap<>(new Comparator<Date>() {
+            @Override
+            public int compare(Date o1, Date o2) {
+                // 降序排列
+                return o1.compareTo(o2);
+            }
+        });//按时间key排序,存放抢救记录、查房记录
+        String recordTime = "", title = "";
+        for (RescueDoc rescueDoc : rescueDocs) {
+            Map<String, String> rescueStructureMap = rescueDoc.getStructureMap();
+            recordTime = rescueStructureMap.get("抢救结束时间");
+            if (StringUtil.isBlank(recordTime)) {
+                continue;
+            }
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null || CatalogueUtil.equalsDate(recordDate, timeOfComparison, "yyyy-MM-dd")) {
+                continue;
+            }
+            dateRecord.put(recordDate, rescueStructureMap);
+        }
+        dateRecord = extractRescue(dateRecord);//24小时内记录只取第一条
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();//查房记录
+        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+            Map<String, String> rescueStructureMap = threeLevelWardDoc.getStructureMap();
+            recordTime = rescueStructureMap.get("查房日期");
+            title = CatalogueUtil.subTitle(rescueStructureMap.get("查房标题"));
+            if (StringUtil.isBlank(recordTime)) {
+                continue;
+            }
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null || StringUtil.isBlank(title) || !title.contains(Content.director)) {//只存副高以上查房记录
+                continue;
+            }
+            dateRecord.put(recordDate, rescueStructureMap);
+        }
+
+        List<Map<String, String>> dateRecordList = new ArrayList<>(dateRecord.values());
+        //如果最后一条是抢救记录,则之后肯定没有查房记录
+        if (dateRecordList.get(dateRecordList.size() - 1).containsKey("抢救结束时间")) {
+            status.set("-1");
+            return;
+        }
+        for (int i = 0; i < dateRecordList.size(); i++) {
+            if (i != dateRecordList.size() - 1) {
+                //当前为抢救记录
+                if (!dateRecordList.get(i).containsKey("抢救结束时间")) {
+                    continue;
+                }
+                //抢救记录下一条不是查房记录则报错
+                if (!dateRecordList.get(i + 1).containsKey("查房日期")) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+    }
+
+    /**
+     * 抽取24小时内第一条记录
+     *
+     * @param dateRecord
+     */
+    private Map<Date, Map<String, String>> extractRescue(Map<Date, Map<String, String>> dateRecord) {
+        Map<Date, Map<String, String>> record = new TreeMap<>(new Comparator<Date>() {
+            @Override
+            public int compare(Date o1, Date o2) {
+                // 降序排列
+                return o1.compareTo(o2);
+            }
+        });
+        Date lastDate = null;
+        for (Map.Entry<Date, Map<String, String>> dateRecordEntry : dateRecord.entrySet()) {
+            Date rescueDate = dateRecordEntry.getKey();
+            if (lastDate == null || CatalogueUtil.compareTime(lastDate, rescueDate, (long) (24 * 60))) {
+                lastDate = rescueDate;
+                record.put(rescueDate, dateRecordEntry.getValue());
+            }
+        }
+        return record;
+    }
+}

+ 138 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0588.java

@@ -0,0 +1,138 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.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.StagesSummaryDoc;
+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.transferrecord.TransferIntoDoc;
+import com.lantone.qc.pub.model.doc.transferrecord.TransferOutDoc;
+import com.lantone.qc.pub.model.doc.transferrecord.TransferRecordDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * @ClassName : THR0588
+ * @Description : 住院期间连续3天无病程记录
+ * @Author : 王世延
+ * @Date: 2020-09-8 14:22
+ */
+@Component
+public class THR0588 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        Date leaveDate = null;
+        if (inputInfo.getMedicalRecordInfoDoc() != null) {
+            Map<String, String> medicalRecordInfoStructureMap = inputInfo.getMedicalRecordInfoDoc().getStructureMap();
+            String leaveHospitalTime = medicalRecordInfoStructureMap.get("leaveHospitalDate");
+            leaveDate = StringUtil.parseDateTime(leaveHospitalTime);
+            if (leaveDate == null) {
+//                LeaveHospitalDoc leaveHospitalDoc = inputInfo.getLeaveHospitalDoc();
+//                if (leaveHospitalDoc != null) {
+//                    leaveDate = StringUtil.parseDateTime(leaveHospitalDoc.getStructureMap().get("出院日期"));
+//                }
+                leaveDate = new Date();
+            }
+        }
+        //查房记录
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        if (threeLevelWardDocs.size() == 0) {
+            return;
+        }
+        //所有查房记录的日期天
+        List<Date> dateThreeLevelDay = new ArrayList<>();
+        StringBuffer sb = new StringBuffer();
+        //所有的查房记录
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (ThreeLevelWardDoc doc : allDoctorWradDocs) {
+            Date threeLevelDate = StringUtil.parseDateTime(doc.getStructureMap().get("查房日期"));
+            if (threeLevelDate == null) {
+                continue;
+            }
+            dateThreeLevelDay.add(threeLevelDate);
+        }
+        /******************************************术后首程********************************************************/
+        if (inputInfo.getOperationDocs().size() > 0) {
+            List<String> recordDateList = inputInfo.getOperationDocs().stream().map(OperationDoc::getOperationDiscussionDoc).filter(Objects::nonNull)
+                    .map(OperationDiscussionDoc::getStructureMap).filter(i -> StringUtil.isNotBlank(i.get("病历日期")))
+                    .map(i -> i.get("病历日期")).collect(Collectors.toList());
+            for (String recordDateStr : recordDateList) {
+                Date recordDate = StringUtil.parseDateTime(recordDateStr);
+                if (recordDate == null) {
+                    continue;
+                }
+                dateThreeLevelDay.add(recordDate);
+            }
+        }
+        /******************************************转入转出********************************************************/
+        if (inputInfo.getTransferRecordDocs() != null) {
+            TransferRecordDoc transferRecordDocs = inputInfo.getTransferRecordDocs();
+            List<String> intoRecordDateList = transferRecordDocs.getTransferIntoDocs()
+                    .stream().map(TransferIntoDoc::getStructureMap).filter(i -> StringUtil.isNotBlank(i.get("病历日期")))
+                    .map(i -> i.get("病历日期")).collect(Collectors.toList());
+            for (String recordDateStr : intoRecordDateList) {
+                Date recordDate = StringUtil.parseDateTime(recordDateStr);
+                if (recordDate == null) {
+                    continue;
+                }
+                dateThreeLevelDay.add(recordDate);
+            }
+            intoRecordDateList = transferRecordDocs.getTransferOutDocs()
+                    .stream().map(TransferOutDoc::getStructureMap).filter(i -> StringUtil.isNotBlank(i.get("病历日期")))
+                    .map(i -> i.get("病历日期")).collect(Collectors.toList());
+            for (String recordDateStr : intoRecordDateList) {
+                Date recordDate = StringUtil.parseDateTime(recordDateStr);
+                if (recordDate == null) {
+                    continue;
+                }
+                dateThreeLevelDay.add(recordDate);
+            }
+        }
+        /******************************************阶段小结********************************************************/
+        if (inputInfo.getStagesSummaryDocs().size() > 0) {
+            List<String> intoRecordDateList = inputInfo.getStagesSummaryDocs().stream().map(StagesSummaryDoc::getStructureMap)
+                    .filter(i -> StringUtil.isNotBlank(i.get("病历日期")))
+                    .map(i -> i.get("病历日期")).collect(Collectors.toList());
+            for (String recordDateStr : intoRecordDateList) {
+                Date recordDate = StringUtil.parseDateTime(recordDateStr);
+                if (recordDate == null) {
+                    continue;
+                }
+                dateThreeLevelDay.add(recordDate);
+            }
+        }
+        dateThreeLevelDay = dateThreeLevelDay.stream().sorted().collect(Collectors.toList());
+        int timeCha = 259200000;
+        //获取连续3天无查房记录的时间
+        for (int i = 0; i < dateThreeLevelDay.size(); i++) {
+            if (i + 1 < dateThreeLevelDay.size()) {
+                if (DateUtil.dateZeroClear(dateThreeLevelDay.get(i + 1)).getTime() - DateUtil.dateZeroClear(dateThreeLevelDay.get(i)).getTime() > timeCha &&
+                        dateThreeLevelDay.get(i + 1).before(leaveDate)) {
+                    infoAppend(sb, dateThreeLevelDay.get(i), dateThreeLevelDay.get(i + 1));
+                }
+            }
+        }
+        if (sb.toString().length() > 0) {
+            status.set("-1");
+            info.set(sb.toString().substring(0, sb.toString().length() - 1));
+        }
+    }
+
+    /**
+     * 拼接提示信息
+     *
+     * @param sb
+     * @param bfDate
+     * @param afDate
+     */
+    private void infoAppend(StringBuffer sb, Date bfDate, Date afDate) {
+        sb.append("(").append(DateUtil.formatDate(bfDate))
+                .append("->").append(DateUtil.formatDate(afDate)).append(")").append("、");
+    }
+}

+ 143 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0601.java

@@ -0,0 +1,143 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+import com.lantone.qc.dbanaly.util.KernelConstants;
+import com.lantone.qc.dbanaly.util.SpecialStorageUtil;
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.kernel.util.CatalogueUtil;
+import com.lantone.qc.pub.Content;
+import com.lantone.qc.pub.model.InputInfo;
+import com.lantone.qc.pub.model.OutputInfo;
+import com.lantone.qc.pub.model.doc.MedicalRecordInfoDoc;
+import com.lantone.qc.pub.model.doc.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.util.DateUtil;
+import com.lantone.qc.pub.util.SpringContextUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @ClassName : THR0601
+ * @Description : 整份病历无主治医师查房记录
+ * @Author : 胡敬
+ * @Date: 2020-03-25 10:21
+ */
+@Component
+public class THR0601 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+
+        MedicalRecordInfoDoc medicalRecordInfoDoc = inputInfo.getMedicalRecordInfoDoc();
+        if (medicalRecordInfoDoc != null && medicalRecordInfoDoc.getStructureMap() != null) {
+            //入院日期
+            String admisTime = medicalRecordInfoDoc.getStructureMap().get("behospitalDate");
+            //出院日期
+            String dischargeTime = medicalRecordInfoDoc.getStructureMap().get("leaveHospitalDate");
+            if (CatalogueUtil.isEmpty(admisTime) || CatalogueUtil.isEmpty(dischargeTime)) {
+                return;
+            }
+            if (!CatalogueUtil.compareTime(
+                    StringUtil.parseDateTime(admisTime),
+                    StringUtil.parseDateTime(DateUtil.nowString()),
+                    Long.valueOf(48 * 60))) {//如果入院未超过48小时,规则不判断
+                return;
+            }
+            //如果住院天数小于2天则不判断该条规则
+            if (DateUtil.parseDate(dischargeTime) != null &&
+                    !CatalogueUtil.compareTime(StringUtil.parseDateTime(admisTime), StringUtil.parseDateTime(dischargeTime), (long) (48 * 60))) {
+                return;
+            } else {
+                if (inputInfo.getThreeLevelWardDocs().size() == 0) {
+                    status.set("-1");
+                    return;
+                }
+            }
+        }
+
+        if (inputInfo.getLeaveHospitalDoc() != null) {
+            Map<String, String> leaveHospitalStructureMap = inputInfo.getLeaveHospitalDoc().getStructureMap();
+            String lengthOfStay = leaveHospitalStructureMap.get("住院天数");
+            if (StringUtil.isNotBlank(lengthOfStay) && CatalogueUtil.numbersOnly(lengthOfStay)) {
+                //如果住院天数小于2天则不判断该条规则
+                if (Integer.parseInt(lengthOfStay) <= 2) {
+                    return;
+                }
+            }
+        }
+
+        /* 如果存在手术记录,判断主刀医生是否为主治医生 */
+        String operatorName = "";
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        if (operationDocs != null) {
+            for (OperationDoc operationDoc : operationDocs) {
+                if (operationDoc.getOperationRecordDoc() != null) {
+                    Map<String, String> operationDocStructureMap = operationDoc.getOperationRecordDoc().getStructureMap();
+                    if (StringUtil.isBlank(operatorName)) {
+                        operatorName = operationDocStructureMap.get("主刀医师");
+                        if (StringUtil.isBlank(operatorName) && StringUtil.isNotBlank(operationDocStructureMap.get("手术者及助手名称"))) {
+                            operatorName = operationDocStructureMap.get("手术者及助手名称").split("、")[0];
+                            if (operatorName.contains("主刀:") && operatorName.split(":").length > 1) {
+                                operatorName = operatorName.split(":")[1];
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        List<ThreeLevelWardDoc> allDoctorWradDocs = inputInfo.getThreeLevelWardDocs().get(0).getAllDoctorWradDocs();//查房记录
+        String title, record;
+        boolean findIndications = false;
+        for (ThreeLevelWardDoc threeLevelWardDoc : allDoctorWradDocs) {
+            Map<String, String> rescueStructureMap = threeLevelWardDoc.getStructureMap();
+            title = CatalogueUtil.subTitle(rescueStructureMap.get("查房标题"));
+            record = CatalogueUtil.subTitle(rescueStructureMap.get("病情记录"));
+            if (StringUtil.isNotBlank(title) && title.contains(Content.attend)) {
+                findIndications = true;
+                break;
+            }
+            if (StringUtil.isNotBlank(record) && record.contains(Content.attend)) {
+                findIndications = true;
+                break;
+            }
+
+            if (StringUtil.isNotBlank(operatorName) && (title.contains("主刀") || record.contains("主刀"))) {
+                String operationProfessor = getCourseProfessor(operatorName);
+                if (operationProfessor.contains("主治")) {
+                    findIndications = true;
+                    break;
+                }
+            }
+
+            //=======如果是三级医师查房算主治医生查房=======
+            if (StringUtil.isNotBlank(operatorName) && (title.contains("三级") || record.contains("三级"))) {
+                findIndications = true;
+                break;
+            }
+
+
+        }
+        if (!findIndications) {
+            status.set("-1");
+        }
+    }
+
+    private String getCourseProfessor(String operatorName) {
+        String professor = "";
+        if (StringUtil.isBlank(operatorName)) {
+            return professor;
+        }
+        SpecialStorageUtil specialStorageUtil = SpringContextUtil.getBean("specialStorageUtil");
+        Map<String, Object> surgeon = specialStorageUtil.getJsonStringValue(KernelConstants.HOSPITAL_DOCTOR_MAP);
+        if (surgeon != null) {
+            Map<String, String> doctor = (Map) surgeon.get(operatorName);
+            if (doctor != null) {
+                professor = doctor.get("professor");
+            }
+
+        }
+        return professor;
+    }
+}

+ 111 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaifuyou/threelevelward/THR0602.java

@@ -0,0 +1,111 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaifuyou.threelevelward;
+
+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.ThreeLevelWardDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationDoc;
+import com.lantone.qc.pub.model.doc.operation.OperationRecordDoc;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+/**
+ * @ClassName : THR0602
+ * @Description : 主刀医师在术后48小时内无查房记录
+ * @Author : 胡敬
+ * @Date: 2020-03-30 14:03
+ */
+@Component
+public class THR0602 extends QCCatalogue {
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        status.set("0");
+        List<OperationDoc> operationDocs = inputInfo.getOperationDocs();
+        List<ThreeLevelWardDoc> threeLevelWardDocs = inputInfo.getThreeLevelWardDocs();
+        long operationCount = operationDocs.stream().filter(operationDoc -> operationDoc.getOperationRecordDoc() != null).count();
+        if (operationCount == 0 || ListUtil.isEmpty(threeLevelWardDocs)) {
+            return;
+        }
+        //所有医师查房记录
+        List<ThreeLevelWardDoc> allDoctorWradDocs = threeLevelWardDocs.get(0).getAllDoctorWradDocs();
+        for (OperationDoc operationDoc : operationDocs) {
+            OperationRecordDoc operationRecordDoc = operationDoc.getOperationRecordDoc();
+            if (operationRecordDoc == null) {
+                continue;
+            }
+            Map<String, String> operationRecordStructureMap = operationRecordDoc.getStructureMap();
+            String operationEndDateStr = operationRecordStructureMap.get("手术结束时间");
+            if (CatalogueUtil.isEmpty(operationEndDateStr)) {
+                continue;
+            }
+
+            //和系统当前时间做比较,如果未超过48小时,条目不做判断
+            Date admisDate = StringUtil.parseDateTime(operationEndDateStr);
+            boolean isExceed = CatalogueUtil.compareTime(admisDate, new Date(), 48 * 60L);
+            if (!isExceed) {
+                return;
+            }
+
+            Map<String, String> doctorRecord = extractWardRecord(
+                    allDoctorWradDocs,
+                    operationEndDateStr,
+                    48 * 60);
+            if(doctorRecord != null && doctorRecord.size()>0){
+                if (!doctorRecord.containsKey("主刀")) {
+                    status.set("-1");
+                    return;
+                }
+            }
+
+        }
+
+
+    }
+
+    /**
+     * 抽取住院duration分钟内查房记录并取第一条主刀医师查房记录
+     *
+     * @param allDoctorWradDocs
+     * @param admisTime
+     * @param duration
+     * @return
+     */
+    private static Map<String, String> extractWardRecord(List<ThreeLevelWardDoc> allDoctorWradDocs, String admisTime, int duration) {
+        Map<Date, String> dateRecord = new HashMap<>();
+        List<String> sortRecord = new ArrayList<>();
+        Map<String, String> doctorRecord = new HashMap<>();
+        String recordTime = "";
+        Date admisDate = StringUtil.parseDateTime(admisTime);
+        if (admisDate == null) {
+            return doctorRecord;
+        }
+        for (ThreeLevelWardDoc attendingDoctorWardDoc : allDoctorWradDocs) {
+            Map<String, String> threeLevelWardStructureMap = attendingDoctorWardDoc.getStructureMap();
+            recordTime = threeLevelWardStructureMap.get("查房日期");
+            Date recordDate = StringUtil.parseDateTime(recordTime);
+            if (recordDate == null) {
+                continue;
+            }
+            if (admisDate.before(recordDate) && !CatalogueUtil.compareTime(admisDate, recordDate, Long.valueOf(duration))) {
+                String wardTitle = threeLevelWardStructureMap.get("查房标题");
+                wardTitle = StringUtil.isBlank(wardTitle) ? "" : wardTitle;
+                dateRecord.put(recordDate, wardTitle);
+            }
+        }
+        dateRecord.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(
+                x -> sortRecord.add(x.getValue())
+        );
+        //按时间排好序查房记录的第一条主刀医师查房记录存进doctorRecord
+        for (String record : sortRecord) {
+            if (!record.contains("主刀")) {
+                continue;
+            }
+            doctorRecord.put("主刀", record);
+            break;
+        }
+        return doctorRecord;
+    }
+}

+ 56 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0024.java

@@ -0,0 +1,56 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.Wound;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 外伤史未填写
+ * @author: rengb
+ * @time: 2020/3/10 13:53
+ */
+@Component
+public class BEH0024 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> behStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructureMap.get("手术外伤史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        List<Wound> wounds = pastLabel.getWounds();
+        if (ListUtil.isNotEmpty(wounds)) {
+            if (wounds.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+        //硬规则匹配
+        String pastLabelText = pastLabel.getText();
+        if (pastLabelText.contains("外伤") || pastLabelText.contains("详见原病历")
+                || pastLabelText.contains("见旧病历") || pastLabelText.contains("见既往病历") || pastLabelText.contains("骨折")) {
+            status.set("0");
+        }
+    }
+}

+ 70 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0025.java

@@ -0,0 +1,70 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.Diag;
+import com.lantone.qc.pub.model.entity.Operation;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 手术史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0025 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> behStructureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructureMap.get("手术外伤史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        List<Operation> operations = pastLabel.getOperations();
+        if (ListUtil.isNotEmpty(operations)) {
+            if (operations.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+                return;
+            }
+        }
+        /* 疾病名称:**术后**,算是有手术史 */
+        List<Diag> diags = pastLabel.getDiags();
+        for (Diag diag : diags) {
+            String hospitalDiagName = diag.getHospitalDiagName();
+            if (StringUtil.isBlank(hospitalDiagName)) {
+                continue;
+            }
+            if (hospitalDiagName.contains("术后")) {
+                status.set("0");
+                return;
+            }
+        }
+        //规则硬匹配
+        String pastLabelText = pastLabel.getText();
+        if (pastLabelText.contains("手术") || pastLabelText.contains("详见原病历")
+                || pastLabelText.contains("见旧病历") || pastLabelText.contains("见既往病历")|| pastLabelText.contains("体外碎石")) {
+            status.set("0");
+        }
+    }
+
+}

+ 60 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0026.java

@@ -0,0 +1,60 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.Allergy;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 食物过敏史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0026 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("过敏史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String pastText = pastLabel.getText();
+        if (pastText.contains("食物过敏史") || pastText.contains("详见原病历")|| pastText.contains("见旧病历") || pastText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        List<Allergy> allergies = pastLabel.getAllergies();
+        if (ListUtil.isNotEmpty(allergies)) {
+            long count = allergies.stream().filter(
+                    i -> i != null
+                            && StringUtil.isNotBlank(i.getName())
+                    //                            && i.getAllergyFood() != null
+                    //                            && StringUtil.isNotBlank(i.getAllergyFood().getName())
+            ).count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+    }
+
+}

+ 54 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0028.java

@@ -0,0 +1,54 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.BloodTransfusion;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 输血史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0028 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> behStructure = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(behStructure.get("输血史"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String pastText = pastLabel.getText();
+        if (pastText.contains("输血") || pastText.contains("详见原病历")|| pastText.contains("见旧病历") || pastText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        List<BloodTransfusion> bloodTransfusions = pastLabel.getBloodTransfusions();
+        if (ListUtil.isNotEmpty(bloodTransfusions)) {
+            if (bloodTransfusions.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                status.set("0");
+            }
+        }
+    }
+
+}

+ 72 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0029.java

@@ -0,0 +1,72 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.BeHospitalizedDoc;
+import com.lantone.qc.pub.model.entity.Vaccinate;
+import com.lantone.qc.pub.model.label.PastLabel;
+import com.lantone.qc.pub.model.label.PersonalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 预防接种史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0029 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        //台州结构化
+        Map<String, String> structureMap = beHospitalizedDoc.getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("预防接种史"))) {
+            status.set("0");
+            return;
+        }
+        //既往史
+        PastLabel pastLabel = beHospitalizedDoc.getPastLabel();
+        //个人史
+        PersonalLabel personalLabel = beHospitalizedDoc.getPersonalLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if ((pastLabel == null || StringUtil.isBlank(pastLabel.getText())) &&
+                (personalLabel == null || StringUtil.isBlank(personalLabel.getText()))) {
+            status.set("0");
+            return;
+        }
+        if (pastLabel != null) {
+            String pastText = pastLabel.getText();
+            if (StringUtil.isNotBlank(pastText) && (pastText.contains("详见原病历") || pastText.contains("预防接种")
+                    || pastText.contains("见旧病历") || pastText.contains("见既往病历"))) {
+                status.set("0");
+                return;
+            }
+            List<Vaccinate> vaccinates = pastLabel.getVaccinates();
+            if (ListUtil.isNotEmpty(vaccinates)) {
+                if (vaccinates.stream().map(i -> i.getName()).filter(i -> StringUtil.isNotBlank(i)).count() > 0) {
+                    status.set("0");
+                    return;
+                }
+            }
+        }
+        if (personalLabel != null) {
+            String personText = personalLabel.getText();
+            if (StringUtil.isNotBlank(personText) && (personText.contains("详见原病历") || personText.contains("预防接种"))) {
+                status.set("0");
+                return;
+            }
+        }
+    }
+}

+ 56 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0030.java

@@ -0,0 +1,56 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @Description: 传染病史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0030 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("结核病")) || StringUtils.isNotEmpty(structureMap.get("病毒性肝炎"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        /* 如果既往史为空或者既往史文本为空,则不报错 */
+        if (pastLabel == null || StringUtil.isBlank(pastLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        if (ListUtil.isNotEmpty(
+                CatalogueUtil.filterDiagsByNature(
+                        pastLabel.getDiags(),
+                        "infectious",
+                        "1"
+                )
+        )) {
+            status.set("0");
+        }
+        String pastText = pastLabel.getText();
+        if (pastText.contains("肝炎") || pastText.contains("结核") || pastText.contains("详见原病历")
+                || pastText.contains("见旧病历") || pastText.contains("见既往病历") || pastText.contains("乙肝")) {
+            status.set("0");
+        }
+    }
+
+}

+ 69 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0031.java

@@ -0,0 +1,69 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+import com.google.common.collect.Lists;
+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.label.PastLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @Description: 慢病史未填写
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0031 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("高血压"))) {
+            status.set("0");
+            return;
+        }
+        PastLabel pastLabel = inputInfo.getBeHospitalizedDoc().getPastLabel();
+        if (pastLabel != null && StringUtil.isNotBlank(pastLabel.getText())) {
+            if (ListUtil.isNotEmpty(
+                    CatalogueUtil.filterDiagsByNature(
+                            pastLabel.getDiags(),
+                            "chronic",
+                            "1"
+                    )
+            )) {
+                status.set("0");
+            }
+            //硬规则匹配
+            String text = pastLabel.getText();
+            if (StringUtils.isNotEmpty(text)) {
+                List<String> words = Lists.newArrayList("高血压", "糖尿病", "阿尔茨海默病", "帕金森", "冠心病心律失常型"
+                        , "冠状动脉性心脏病", "冠状动脉粥样硬化性心脏病", "慢性肾炎综合征", "肾病", "慢性肾衰竭", "肾功能异常", "哮喘"
+                        , "肺结核", "腹膜透析", "慢性阻塞性肺病", "精神分裂症", "分裂情感性精神病", "双相情感障碍,目前为缓解状态", "前列腺增生"
+                        , "高脂血症", "高低密度脂蛋白胆固醇血症", "高胆固醇血症", "高甘油三酯血症", "骨质疏松", "慢性乙型病毒性肝炎"
+                        , "慢性庚型肝炎", "慢性肝炎", "慢性丁型肝炎", "慢性病毒性肝炎", "慢性丙型病毒性肝炎", "酒精性肝病", "脂肪肝"
+                        , "肝硬化", "肝恶性肿瘤", "肝恶性细胞瘤", "肝病", "类风湿性关节炎", "心脑血管", "内分泌","见旧病历","见既往病历");
+                for (String word : words) {
+                    if (text.contains(word)) {
+                        status.set("0");
+                        return;
+                    }
+                }
+            }
+        } else {
+            status.set("0");
+            return;
+        }
+    }
+
+}

+ 63 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0042.java

@@ -0,0 +1,63 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.Address;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 出生地未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0042 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if(inputInfo.getBeHospitalizedDoc() == null){
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("出生地"))) {
+            status.set("0");
+            return;
+        }
+
+        String text = inputInfo.getBeHospitalizedDoc().getPersonalLabel().getText();
+        if (StringUtil.isBlank(text)) {
+            return;
+        }
+        List<Address> addresses = inputInfo.getBeHospitalizedDoc().getPersonalLabel().getAddresses();
+        if (ListUtil.isNotEmpty(addresses)) {
+            long count = addresses.stream().filter(i -> {
+                boolean flag = false;
+                if (i != null && StringUtil.isNotBlank(i.getName())) {
+                    if (i.getName().indexOf("出生") > -1) {
+                        flag = true;
+                    } else {
+                        Pattern pattern = Pattern.compile("[\\s\\S]*(出生|生长)[\\s\\S]{0,5}" + i.getName() + "[\\s\\S]*");
+                        flag = pattern.matcher(text).matches();
+                    }
+                }
+                return flag;
+            }).count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+        if (text.contains("出生")|| text.contains("见旧病历") || text.contains("见既往病历")) {
+            status.set("0");
+        }
+    }
+
+}

+ 70 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0043.java

@@ -0,0 +1,70 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.Address;
+import com.lantone.qc.pub.model.label.PersonalLabel;
+import com.lantone.qc.pub.util.ListUtil;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * @Description: 居住地未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0043 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        //台州结构化
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("联系地址")) || StringUtils.isNotEmpty(structureMap.get("现住址"))
+                || StringUtils.isNotEmpty(structureMap.get("家庭住址"))) {
+            status.set("0");
+            return;
+        }
+
+        PersonalLabel personalLabel = inputInfo.getBeHospitalizedDoc().getPersonalLabel();
+        if (personalLabel == null) {
+            status.set("0");
+            return;
+        }
+        String text = personalLabel.getText();
+        if (StringUtil.isBlank(text) || text.contains("居住") || text.contains("生长") || text.contains("生活") || text.contains("本地")
+                || text.contains("详见原病历")|| text.contains("见旧病历") || text.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        List<Address> addresses = personalLabel.getAddresses();
+        if (ListUtil.isNotEmpty(addresses)) {
+            long count = addresses.stream().filter(i -> {
+                boolean flag = false;
+                if (i != null && StringUtil.isNotBlank(i.getName())) {
+                    if (i.getName().indexOf("居住") > -1) {
+                        flag = true;
+                    } else {
+                        Pattern pattern = Pattern.compile("[\\s\\S]*(居住|生长)[\\s\\S]{0,5}" + i.getName() + "[\\s\\S]*");
+                        flag = pattern.matcher(text).matches();
+                    }
+                }
+                return flag;
+            }).count();
+            if (count > 0) {
+                status.set("0");
+            }
+        }
+    }
+
+}

+ 53 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0047.java

@@ -0,0 +1,53 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+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.entity.Drinking;
+import com.lantone.qc.pub.model.label.PersonalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @Description: 饮酒史未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0047 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        if (inputInfo.getBeHospitalizedDoc() == null) {
+            status.set("0");
+            return;
+        }
+        Map<String, String> structureMap = inputInfo.getBeHospitalizedDoc().getStructureMap();
+        if (StringUtils.isNotEmpty(structureMap.get("饮酒"))) {
+            status.set("0");
+            return;
+        }
+        PersonalLabel personalLabel = inputInfo.getBeHospitalizedDoc().getPersonalLabel();
+        if (personalLabel == null || StringUtil.isBlank(personalLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String pastText = personalLabel.getText();
+        if (pastText.contains("饮酒") || pastText.contains("详见原病历")|| pastText.contains("见旧病历") || pastText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+
+        Drinking drinking = personalLabel.getDrinking();
+        if (drinking != null && StringUtil.isNotBlank(drinking.getName())) {
+            status.set("0");
+        }
+        //硬匹配规则
+        if (inputInfo.getBeHospitalizedDoc().getPersonalLabel().getText().contains("酒")) {
+            status.set("0");
+        }
+    }
+}

+ 65 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0053.java

@@ -0,0 +1,65 @@
+package com.lantone.qc.kernel.catalogue.hospital.ninghaiyiyi.behospitalized;
+
+import com.lantone.qc.kernel.catalogue.QCCatalogue;
+import com.lantone.qc.pub.Content;
+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.entity.Marryiage;
+import com.lantone.qc.pub.model.label.MaritalLabel;
+import com.lantone.qc.pub.util.StringUtil;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+
+/**
+ * @Description: 结婚年龄未描述
+ * @author: rengb
+ * @time: 2020/3/10 14:02
+ */
+@Component
+public class BEH0053 extends QCCatalogue {
+
+    @Override
+    public void start(InputInfo inputInfo, OutputInfo outputInfo) {
+        BeHospitalizedDoc beHospitalizedDoc = inputInfo.getBeHospitalizedDoc();
+        if (beHospitalizedDoc == null) {
+            status.set("0");
+            return;
+        }
+        //先取一次结构化数据
+        Map<String, String> beHospitalizedStructureMap = beHospitalizedDoc.getStructureMap();
+        String marryiAgeStr = beHospitalizedStructureMap.get("结婚年龄");
+        if (StringUtil.isNotBlank(marryiAgeStr)) {
+            status.set("0");
+            return;
+        }
+        //硬规则 匹配未婚
+        String marry = beHospitalizedDoc.getStructureMap().get(Content.marry);
+        if (StringUtil.isBlank(marry)) {
+            marry = beHospitalizedDoc.getStructureMap().get("婚姻状况");
+        }
+        MaritalLabel maritalLabel = beHospitalizedDoc.getMaritalLabel();
+        if (maritalLabel == null || StringUtil.isBlank(maritalLabel.getText())) {
+            status.set("0");
+            return;
+        }
+        String maritalText = maritalLabel.getText();
+        if ("未婚".equals(marry) || maritalText.contains("未婚") || maritalText.contains("详见原病历")
+                || maritalText.contains("离婚") || maritalText.contains("离异") || maritalText.contains("结婚")
+                || maritalText.contains("丧偶")|| maritalText.contains("见旧病历") || maritalText.contains("见既往病历")) {
+            status.set("0");
+            return;
+        }
+        if (beHospitalizedDoc.getMaritalLabel() == null
+                || StringUtil.isBlank(beHospitalizedDoc.getMaritalLabel().getText())) {
+            status.set("0");
+        }
+
+        Marryiage marryiage = beHospitalizedDoc.getMaritalLabel().getMarryiage();
+        if (marryiage != null && StringUtil.isNotBlank(marryiage.getName())) {
+            status.set("0");
+        }
+    }
+
+}

+ 0 - 0
kernel/src/main/java/com/lantone/qc/kernel/catalogue/hospital/ninghaiyiyi/behospitalized/BEH0058.java


Some files were not shown because too many files changed in this diff