YiZhuCheckData.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. package thyyxxk.webserver.service.zhuyuanyisheng.yizhuverify;
  2. import cn.hutool.core.convert.Convert;
  3. import cn.hutool.core.util.StrUtil;
  4. import cn.hutool.extra.spring.SpringUtil;
  5. import lombok.Getter;
  6. import lombok.extern.slf4j.Slf4j;
  7. import org.jetbrains.annotations.NotNull;
  8. import thyyxxk.webserver.config.envionment.YzConfig;
  9. import thyyxxk.webserver.constants.Capacity;
  10. import thyyxxk.webserver.dao.his.zhuyuanyisheng.YiZhuLuRuDao;
  11. import thyyxxk.webserver.entity.login.UserInfo;
  12. import thyyxxk.webserver.entity.yzcheck.ChargeLimitations;
  13. import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.XinZhenYiZhu;
  14. import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.XinZhenYzActOrder;
  15. import thyyxxk.webserver.entity.zhuyuanyisheng.yizhuluru.YzZdOrderItemConfirm;
  16. import thyyxxk.webserver.service.hutoolcache.ExtraCache;
  17. import thyyxxk.webserver.utils.*;
  18. import java.util.*;
  19. import java.util.function.Consumer;
  20. @Slf4j
  21. public class YiZhuCheckData {
  22. private final YiZhuLuRuDao dao;
  23. private final ExtraCache extraCache = SpringUtil.getBean(ExtraCache.class);
  24. private final YzConfig yzConfig = SpringUtil.getBean(YzConfig.class);
  25. /**
  26. * 医嘱数组
  27. */
  28. private List<XinZhenYzActOrder> list;
  29. /**
  30. * 药品
  31. */
  32. private final CacheOnce<XinZhenYzActOrder> drug = new CacheOnce<>();
  33. /**
  34. * 医生等级
  35. */
  36. private final CacheOnce<Integer> authorizedDoctorLevel = new CacheOnce<>();
  37. /**
  38. * 项目
  39. */
  40. private final CacheOnce<List<XinZhenYzActOrder>> project = new CacheOnce<>();
  41. /**
  42. * 医嘱
  43. */
  44. private final CacheOnce<XinZhenYzActOrder> yzItem = new CacheOnce<>();
  45. /**
  46. * 医保限制
  47. */
  48. private final CacheOnce<ChargeLimitations> yiBaoLimitation = new CacheOnce<>();
  49. /**
  50. * 患者信息
  51. */
  52. private XinZhenYiZhu patientInformation;
  53. private Boolean strictVerification = false;
  54. /**
  55. * 错误信息
  56. */
  57. List<String> errorMessage = new ArrayList<>();
  58. /**
  59. * 警告信息
  60. */
  61. List<String> warningMessage = new ArrayList<>();
  62. Map<String, Object> returnMap = new HashMap<>();
  63. private UserInfo userInfo;
  64. @Getter
  65. private Boolean passTheAudit = true;
  66. private final List<String> groupList = Arrays.asList("71", "73");
  67. private boolean checkForRestrictedMedication = false;
  68. /**
  69. * 全排斥医嘱
  70. */
  71. @Getter
  72. private Repel repel = null;
  73. private final String ONCE = "ONCE";
  74. public YiZhuCheckData(YiZhuLuRuDao dao) {
  75. this.dao = dao;
  76. }
  77. public YiZhuCheckData init(List<XinZhenYzActOrder> list, XinZhenYiZhu patientInformation) {
  78. userInfo = dao.selectedUserInfoByCode(TokenUtil.getInstance().getTokenUserId());
  79. this.list = list;
  80. this.patientInformation = patientInformation;
  81. strictVerification = patientInformation != null;
  82. return this;
  83. }
  84. public YiZhuCheckData init(XinZhenYzActOrder orderData, XinZhenYiZhu patientInformation) {
  85. List<XinZhenYzActOrder> temp = Collections.singletonList(orderData);
  86. init(temp, patientInformation);
  87. return this;
  88. }
  89. public void judgeExclusion() {
  90. repel = new Repel();
  91. }
  92. public Map<String, Object> startCheck(Consumer<XinZhenYzActOrder> action) {
  93. for (XinZhenYzActOrder item : list) {
  94. clearErrorMessage();
  95. if (strictVerification) {
  96. strictVerificationFunc(item);
  97. }
  98. publicCheck(item);
  99. String ITEM = "00";
  100. if (item.getSerial().equals(ITEM)) {
  101. itemCheck(item);
  102. } else {
  103. drugCheck(item);
  104. }
  105. if (action != null) {
  106. action.accept(item);
  107. }
  108. setReturnMap(item.getActOrderNo().stripTrailingZeros().toPlainString());
  109. }
  110. return getReturnMap();
  111. }
  112. /**
  113. * 公共校验
  114. *
  115. * @param item 医嘱
  116. */
  117. private void publicCheck(XinZhenYzActOrder item) {
  118. if (StringUtil.isBlank(item.getOrderCode())) {
  119. errorMessage.add("项目编码不能为空");
  120. }
  121. if (StringUtil.isBlank(item.getOrderName())) {
  122. errorMessage.add("项目名称不能为空");
  123. }
  124. if (StringUtil.isBlank(item.getExecUnit())) {
  125. errorMessage.add("执行科室不能为空");
  126. } else if (item.getExecUnit().startsWith("8")) {
  127. errorMessage.add("执行科室不能选择为病区");
  128. }
  129. if (StringUtil.isBlank(item.getFrequCode())) {
  130. errorMessage.add("频次不能为空");
  131. } else if (dao.getTheFrequency(item.getFrequCode()) == 0) {
  132. errorMessage.add("该执行频率已被停用,请更改,不然会导致无法执行。");
  133. }
  134. }
  135. private void itemCheck(XinZhenYzActOrder item) {
  136. List<XinZhenYzActOrder> projectDetails = project.get(item.getOrderCode(), dao::itemDataOne);
  137. XinZhenYzActOrder yzItemInio = yzItem.get(item.getOrderCode(), dao::yzItem);
  138. if (yzItemInio != null) {
  139. if (yzItemInio.getDelFlag() == 1) {
  140. errorMessage.add("该医嘱已被停用,请联系物价。");
  141. }
  142. }
  143. List<String> ZK_CODE_LIST = new ArrayList<>();
  144. Map<String, YzZdOrderItemConfirm> specialMedicalAdvice = new HashMap<>();
  145. List<YzZdOrderItemConfirm> value = extraCache.getYzZdOrderItemConfirm();
  146. if (value != null) {
  147. for (YzZdOrderItemConfirm yzCode : value) {
  148. if (yzCode.getItemName().equals("转科")) {
  149. ZK_CODE_LIST.add(yzCode.getOrderCode());
  150. }
  151. specialMedicalAdvice.put(yzCode.getOrderCode(), yzCode);
  152. }
  153. }
  154. if (strictVerification && specialMedicalAdvice.containsKey(item.getOrderCode())) {
  155. YzZdOrderItemConfirm confirm = specialMedicalAdvice.get(item.getOrderCode());
  156. if (confirm.getCount() > 0 && dao.selectCountByOrderCode(patientInformation.getInpatientNo(), patientInformation.getAdmissTimes(), item.getOrderCode()) > confirm.getCount()) {
  157. errorMessage.add("该医嘱只能开一条,请先作废上一条医嘱");
  158. }
  159. }
  160. if (ZK_CODE_LIST.contains(item.getOrderCode())) {
  161. if (StringUtil.isBlank(item.getZkWardCode())) {
  162. errorMessage.add("转科病房不能为空");
  163. }
  164. if (StringUtil.isBlank(item.getZkDeptCode())) {
  165. errorMessage.add("转科科室不能为空");
  166. }
  167. }
  168. // 如果这个项目下面没有费用明细,那么这就是一条口头医嘱,或者特殊医嘱
  169. if (ListUtil.notBlank(projectDetails)) {
  170. for (XinZhenYzActOrder detailed : projectDetails) {
  171. if (detailed.getDelFlag() == 1) {
  172. errorMessage.add(String.format("项目:【%s】,已经被物价停用了", detailed.getOrderName()));
  173. }
  174. if (StringUtil.isBlank(detailed.getNationalCode())) {
  175. warningMessage.add(String.format("项目:【%s】,没有匹配医保码", detailed.getOrderName()));
  176. }
  177. }
  178. }
  179. // 是否是排斥医嘱
  180. if (repel != null) {
  181. Integer paiChiYiZhu = dao.shiFouPaiChiYiZhu(item.getOrderCode());
  182. if (paiChiYiZhu != null && paiChiYiZhu.equals(1)) {
  183. repel.setCount(repel.getCount() + 1);
  184. repel.setOrderNo(item.getActOrderNo());
  185. repel.setDate(item.getStartTime());
  186. }
  187. if (yzConfig.getExceedingDischargeDays() != -1) {
  188. List<String> strings = extraCache.getYzZdOrderItemMap().get("出院");
  189. if (strings.contains(item.getOrderCode())) {
  190. Date admissDate = cn.hutool.core.date.DateUtil.beginOfDay(patientInformation.getAdmissDate());
  191. Date orderTime = cn.hutool.core.date.DateUtil.beginOfDay(item.getOrderTime());
  192. long betweenDay = cn.hutool.core.date.DateUtil.betweenDay(orderTime, admissDate, false);
  193. if (betweenDay <= yzConfig.getExceedingDischargeDays()) {
  194. if (item.getSuperiorDoctor() == null || !item.getSuperiorDoctor().equals(patientInformation.getDeptDirector())) {
  195. errorMessage.add("患者的住院天数小于等于4请科主任授权,开出院医嘱。");
  196. }
  197. }
  198. }
  199. }
  200. }
  201. item.setDrugOcc(item.getDrugQuan());
  202. }
  203. private void drugCheck(XinZhenYzActOrder item) {
  204. if (StringUtil.isBlank(item.getGroupNo())) {
  205. errorMessage.add("没有药房请重新开。");
  206. return;
  207. }
  208. if (!groupList.contains(item.getGroupNo())) {
  209. errorMessage.add("药房错误,请重新选择药房。");
  210. }
  211. String key = item.getOrderCode().trim() + item.getSerial().trim() + item.getGroupNo().trim();
  212. XinZhenYzActOrder detailsOfDrugs = getDrugData(key, item.getSupplyCode());
  213. if (BigUtils.bigXiaoYu(item.getDose(), 0)) {
  214. errorMessage.add(String.format("医嘱:【%s】,计量不能开负数", item.getOrderName()));
  215. }
  216. if (StringUtil.isBlank(item.getDrugSpecification())) {
  217. errorMessage.add("药品规格不能为空");
  218. }
  219. if (StringUtil.isBlank(item.getSupplyCode())) {
  220. errorMessage.add("给药方式不能为空");
  221. } else if (detailsOfDrugs.getSupplyCode() == null || "1".equals(detailsOfDrugs.getSupplyCode())) {
  222. errorMessage.add("给药方式已被停用。");
  223. }
  224. if (item.getDose() == null || BigUtils.dengYu(item.getDose(), 0)) {
  225. errorMessage.add("一次计量不能为空");
  226. }
  227. if (StringUtil.isBlank(item.getDoseUnit())) {
  228. errorMessage.add("计量单位不能为空");
  229. }
  230. if (StringUtil.isBlank(item.getSerial())) {
  231. errorMessage.add("包装大小不能为空");
  232. }
  233. if (detailsOfDrugs == null) {
  234. errorMessage.add("没有找到对应的药品,请联系药房。");
  235. return;
  236. }
  237. if (StringUtil.isBlank(item.getMiniUnit())) {
  238. if (item.getSerial().equals("01")) {
  239. item.setMiniUnit(detailsOfDrugs.getMiniUnit());
  240. } else {
  241. item.setMiniUnit(detailsOfDrugs.getPackUnit());
  242. }
  243. }
  244. if (detailsOfDrugs.getDelFlag() == 1) {
  245. errorMessage.add("药品已经被停用了,请联系药剂科");
  246. }
  247. if (detailsOfDrugs.getYpLevel() > userInfo.getDoctorLevel()) {
  248. Integer superiorPhysicianRank = authorizedDoctorLevel.get(item.getSuperiorDoctor(), (code) -> {
  249. Integer yiShenDengJi = dao.huoQuYiShenDengJi(code);
  250. return yiShenDengJi == null ? 0 : yiShenDengJi;
  251. });
  252. if (detailsOfDrugs.getYpLevel() > superiorPhysicianRank) {
  253. errorMessage.add("您没有开此药品的权限");
  254. }
  255. }
  256. if (StringUtil.isBlank(detailsOfDrugs.getNationalCode())) {
  257. warningMessage.add("该药品没有医保编码");
  258. }
  259. if (BigUtils.bigXiaoYu(detailsOfDrugs.getStockAmount(), 10)) {
  260. warningMessage.add(String.format("该药品剩余数量为:【%s】", detailsOfDrugs.getStockAmount().stripTrailingZeros().toPlainString()));
  261. }
  262. if (BigUtils.bigDaYu(item.getDrugQuan(), detailsOfDrugs.getStockAmount())) {
  263. errorMessage.add("药品领量大于药品的库存,当前库存量" + detailsOfDrugs.getStockAmount().stripTrailingZeros().toPlainString());
  264. }
  265. // 严格校验
  266. if (strictVerification) {
  267. if (detailsOfDrugs.getDeptRestrictions() > 0) {
  268. errorMessage.add("该药品禁止在患者所在的科室使用。");
  269. }
  270. if (detailsOfDrugs.getVisibleFlagZy() == 1) {
  271. errorMessage.add("该药品禁止住院患者使用。");
  272. }
  273. item.setKjywFlag(detailsOfDrugs.getKjywFlag());
  274. if (detailsOfDrugs.getKjywFlag() == 1) {
  275. if (item.getYyfs() == null) {
  276. errorMessage.add("请填写抗菌药物医嘱附注信息录入");
  277. } else if (item.getYyfs() == 1 || item.getYyfs() == 2) {
  278. if (item.getSsqk() == null) {
  279. errorMessage.add("当用药方式为 1 或 2 时,手术切口和用药时间不能为空");
  280. }
  281. }
  282. }
  283. // 校验医保限制用药(item);
  284. }
  285. String 出院带药 = "007";
  286. if (item.getSupplyCode() != null && 出院带药.equals(item.getSupplyCode())) {
  287. if (!ONCE.equals(item.getFrequCode())) {
  288. errorMessage.add("出院带药不能是长期医嘱。");
  289. }
  290. item.setDrugOcc(item.getDrugQuan());
  291. } else {
  292. // 计算普通药品的领量 durg_quan durg_occ
  293. calculateDrugAmount(item, detailsOfDrugs);
  294. if (item.getDrugOcc() == null) {
  295. errorMessage.add("医嘱领量错误,请重新选择【剂量单位】,或这可能药房剂量维护错误。");
  296. }
  297. }
  298. }
  299. void 校验医保限制用药(XinZhenYzActOrder item) {
  300. if (!checkForRestrictedMedication) return;
  301. // 自费不管
  302. if ("1".equals(item.getYbSelfFlag())) return;
  303. ChargeLimitations chargeLimitations = yiBaoLimitation.get(item.getOrderCode(), dao::hasCharge);
  304. if (chargeLimitations == null) {
  305. return;
  306. }
  307. List<ChargeLimitations> data = dao.getChargeDay(patientInformation.getInpatientNo(),
  308. patientInformation.getAdmissTimes(),
  309. patientInformation.getLedgerSn(),
  310. item.getOrderCode());
  311. // 已经使用
  312. int usedAlready = ListUtil.isBlank(data) ? 0 : data.size();
  313. //可以使用天数
  314. int canBeUsed = chargeLimitations.getLimitDay() - usedAlready;
  315. Date latestNumberOfDays = DateUtil.addNaturalDays(item.getStartTime(), canBeUsed);
  316. int compare = DateUtil.compare(item.getEndTime(), latestNumberOfDays);
  317. if (item.getEndTime() == null) {
  318. errorMessage.add(StrUtil.format(
  319. "医保限制:【{}】天,请设置停止时间,不得超过【{}】",
  320. chargeLimitations.getLimitDay(), DateUtil.formatDate(latestNumberOfDays, DateUtil.DEFAULT_PATTERN)));
  321. } else if (compare > 0) {
  322. errorMessage.add(StrUtil.format(
  323. "医保限制:【{}】天,请设置停止时间,不得超过【{}】",
  324. chargeLimitations.getLimitDay(), DateUtil.formatDate(latestNumberOfDays, DateUtil.DEFAULT_PATTERN)));
  325. }
  326. }
  327. private void strictVerificationFunc(@NotNull XinZhenYzActOrder item) {
  328. if (item.getParentNo() != null && item.getActOrderNo() != null) {
  329. if (BigUtils.bigXiaoYu(item.getActOrderNo(), item.getParentNo())) {
  330. errorMessage.add("子医嘱的医嘱号,小于父医嘱号,无法成组。【原因如果子医嘱的医嘱号小于父医嘱,会导致打印乱码】");
  331. }
  332. }
  333. // 获取患者的入院时间
  334. if (item.getOrderTime() == null) {
  335. errorMessage.add("医嘱时间不能为空");
  336. } else if (item.getStartTime() == null) {
  337. errorMessage.add("开始时间不能为空");
  338. } else if (patientInformation.getAdmissDate() == null) {
  339. errorMessage.add("没有查询到患者的入院时间");
  340. } else if (DateUtil.shiJianDaXiao(item.getStartTime(), patientInformation.getAdmissDate(), "<")) {
  341. errorMessage.add("开始时间不能在患者入院之前,患者入院时间" + DateUtil.formatDatetime(patientInformation.getAdmissDate()));
  342. } else if (DateUtil.shiJianDaXiao(item.getStartTime(), item.getOrderTime(), "<") && !userInfo.getDeptCode().equals("1160000")) {
  343. errorMessage.add("开始时间不能在开医嘱之前");
  344. }
  345. if (item.getEndTime() != null) {
  346. if (ONCE.equals(item.getFrequCode().trim())) {
  347. item.setEndTime(null);
  348. } else {
  349. if (DateUtil.shiJianDaXiao(item.getEndTime(), item.getStartTime(), "<")) {
  350. errorMessage.add("结束时间不能在开始时间之前");
  351. } else {
  352. item.setModifier(TokenUtil.getInstance().getTokenUserId());
  353. }
  354. }
  355. } else {
  356. item.setModifier(null);
  357. }
  358. }
  359. public XinZhenYzActOrder getDrugData(String code) {
  360. return drug.get(code, (temp) -> dao.drugDataOne(temp, patientInformation != null ? patientInformation.getZkWard() : "", null));
  361. }
  362. public XinZhenYzActOrder getDrugData(String code, String supplyCode) {
  363. return drug.get(code, (temp) -> dao.drugDataOne(temp, patientInformation != null ? patientInformation.getZkWard() : "", supplyCode));
  364. }
  365. public static void calculateDrugAmount(XinZhenYzActOrder item, XinZhenYzActOrder feiYongXinXi) {
  366. if (feiYongXinXi == null) {
  367. return;
  368. }
  369. item.setDrugWeight(feiYongXinXi.getDrugWeight());
  370. item.setDrugWeightUnit(feiYongXinXi.getDrugWeightUnit());
  371. item.setDrugVolume(feiYongXinXi.getDrugVolume());
  372. item.setDrugVolUnit(feiYongXinXi.getDrugWeightUnit());
  373. String doseUnit = StringUtil.isBlank(item.getDoseUnit()) ? "" : item.getDoseUnit().trim();
  374. if (StringUtil.notBlank(feiYongXinXi.getDrugWeightUnit()) && doseUnit.equals(feiYongXinXi.getDrugWeightUnit().trim())) {
  375. item.setDrugOcc(DecimalUtil.divide(item.getDose(), feiYongXinXi.getDrugWeight(), 2));
  376. } else if (StringUtil.notBlank(feiYongXinXi.getDrugVolUnit()) && doseUnit.equals(feiYongXinXi.getDrugVolUnit().trim())) {
  377. item.setDrugOcc(DecimalUtil.divide(item.getDose(), feiYongXinXi.getDrugVolume(), 2));
  378. } else if (StringUtil.notBlank(feiYongXinXi.getPackUnit()) && doseUnit.equals(feiYongXinXi.getPackUnit().trim())) {
  379. item.setDrugOcc(DecimalUtil.divide(item.getDose(), feiYongXinXi.getPackSize(), 2));
  380. }
  381. }
  382. private void clearErrorMessage() {
  383. errorMessage.clear();
  384. warningMessage.clear();
  385. }
  386. private void setReturnMap(String orderNo) {
  387. Map<String, Object> map = new HashMap<>(Capacity.TWO);
  388. if (ListUtil.notBlank(errorMessage)) {
  389. List<String> list = new ArrayList<>(errorMessage);
  390. map.put("error", list);
  391. passTheAudit = false;
  392. }
  393. if (ListUtil.notBlank(warningMessage)) {
  394. List<String> list = new ArrayList<>(warningMessage);
  395. map.put("warning", list);
  396. }
  397. if (!map.isEmpty()) {
  398. returnMap.put(orderNo, map);
  399. }
  400. }
  401. private Map<String, Object> getReturnMap() {
  402. return returnMap;
  403. }
  404. public Boolean getFailed() {
  405. return !passTheAudit;
  406. }
  407. public Boolean multipleExclusions() {
  408. if (repel == null) {
  409. return false;
  410. }
  411. return repel.getCount() > 1;
  412. }
  413. public YiZhuCheckData checkForRestrictedMedication() {
  414. checkForRestrictedMedication = true;
  415. return this;
  416. }
  417. }