PreAndInProcessAnalysisLogService.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. using Newtonsoft.Json.Linq;
  2. using PTMedicalInsurance.Helper;
  3. using PTMedicalInsurance.Variables;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Windows.Forms;
  7. namespace PTMedicalInsurance.Business
  8. {
  9. /// <summary>
  10. /// 事前事中分析违规日志服务(适配宽表 BS_PreInProcess_ViolationLog)
  11. /// </summary>
  12. public class PreAndInProcessAnalysisLogService
  13. {
  14. private InvokeHelper invoker = new InvokeHelper();
  15. /// <summary>
  16. /// 记录3101/3102交易违规日志(逐条写入宽表)
  17. /// </summary>
  18. /// <param name="tradeCode">交易代码(3101,3102)</param>
  19. /// <param name="tradeName">交易名称</param>
  20. /// <param name="triggerScene">触发场景</param>
  21. /// <param name="inputData">输入数据</param>
  22. /// <param name="outputData">输出数据(含违规结果)</param>
  23. /// <param name="errMsg">错误信息</param>
  24. /// <returns>0:成功, -1:失败</returns>
  25. public int LogPreInProcessAnalysis(string tradeCode, string tradeName, string triggerScene,
  26. JObject inputData, JObject outputData, out string errMsg)
  27. {
  28. errMsg = "";
  29. try
  30. {
  31. JObject joData = inputData["data"] as JObject;
  32. if (joData == null)
  33. {
  34. errMsg = "输入数据格式不正确";
  35. return -1;
  36. }
  37. JArray jaPatientDtos = joData["patient_dtos"] as JArray;
  38. if (jaPatientDtos == null || jaPatientDtos.Count == 0)
  39. {
  40. errMsg = "病人信息为空";
  41. return -1;
  42. }
  43. // 提取交易公共信息
  44. string taskId = joData["task_id"]?.ToString() ?? "";
  45. string msgId = inputData["msgid"]?.ToString() ?? "";
  46. string opterCode = inputData["opter"]?.ToString() ?? Global.user.ID;
  47. string opterName = inputData["opter_name"]?.ToString() ?? Global.user.name;
  48. string infTimeStr = inputData["inf_time"]?.ToString() ?? DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  49. DateTime infTime = DateTime.TryParse(infTimeStr, out var dtTmp) ? dtTmp : DateTime.Now;
  50. // 解析违规结果列表(支持对象/数组/judge_result_dtos嵌套)
  51. List<JObject> violationList = ParseViolationList(outputData);
  52. if (violationList.Count == 0)
  53. {
  54. Global.writeLog($"{tradeCode} 未返回违规结果,无需记录");
  55. return 0;
  56. }
  57. int successCount = 0;
  58. foreach (JObject joVio in violationList)
  59. {
  60. string jrId = joVio["jr_id"]?.ToString() ?? "";
  61. if (string.IsNullOrEmpty(jrId))
  62. {
  63. Global.writeLog($"{tradeCode} 违规结果缺少 jr_id,跳过记录");
  64. continue;
  65. }
  66. JObject joLog = BuildViolationLog(joVio, jaPatientDtos, taskId, tradeCode, tradeName,
  67. triggerScene, msgId, opterCode, opterName, infTime);
  68. JObject joParam = new JObject();
  69. joParam.Add("LogData", joLog);
  70. joParam.Add("HospitalDr", Global.inf.hospitalDr);
  71. joParam.Add("InterfaceDr", Global.inf.interfaceDr);
  72. joParam.Add("UpdateUserID", Global.user.ID);
  73. string serviceCode = "09010148";
  74. string inpar = JsonHelper.setIrisInpar(serviceCode, joParam).ToString();
  75. JObject joRtn = invoker.invokeInsuService(inpar, "记录事前事中违规日志");
  76. if (JsonHelper.parseIrisRtnValue(joRtn, out string insertErr) == 0)
  77. {
  78. successCount++;
  79. }
  80. else
  81. {
  82. Global.writeLog($"记录违规日志失败(JrID={jrId}): {insertErr}");
  83. }
  84. }
  85. Global.writeLog($"记录{tradeCode}违规日志: 成功{successCount}/{violationList.Count}");
  86. return successCount > 0 ? 0 : -1;
  87. }
  88. catch (Exception ex)
  89. {
  90. errMsg = $"记录日志异常: {ex.Message}";
  91. Global.writeLog($"记录{tradeCode}日志异常: {ex.Message}");
  92. return -1;
  93. }
  94. }
  95. /// <summary>
  96. /// 记录3103反馈日志(根据 warn_rslt_id 更新宽表反馈字段)
  97. /// 说明:3103的 warn_rslt_id 与 3101/3102 的 jr_id 为同一值,均映射到宽表的 JrID 字段
  98. /// </summary>
  99. /// <param name="warningType">反馈类型(1:事前,2:事中)</param>
  100. /// <param name="warnings">反馈数据集合</param>
  101. /// <param name="errMsg">错误信息</param>
  102. /// <returns>0:成功, -1:失败</returns>
  103. public int LogFeedback(string warningType, JArray warnings, out string errMsg)
  104. {
  105. errMsg = "";
  106. try
  107. {
  108. if (warnings == null || warnings.Count == 0)
  109. {
  110. errMsg = "反馈数据为空";
  111. return -1;
  112. }
  113. int successCount = 0;
  114. foreach (JObject joWarn in warnings)
  115. {
  116. string warnRsltId = joWarn["warn_rslt_id"]?.ToString() ?? "";
  117. string dspoWay = joWarn["dspo_way"]?.ToString() ?? "";
  118. string dspoWayRea = joWarn["dspo_way_rea"]?.ToString() ?? "";
  119. if (string.IsNullOrEmpty(warnRsltId))
  120. {
  121. continue;
  122. }
  123. JObject joLog = new JObject();
  124. joLog.Add("JrID", warnRsltId); // 对应宽表 JrID
  125. joLog.Add("DspoWay", dspoWay);
  126. joLog.Add("DspoWayRea", dspoWayRea);
  127. joLog.Add("WarnType", warningType);
  128. joLog.Add("FeedbackDate", DateTime.Now.ToString("yyyy-MM-dd"));
  129. joLog.Add("FeedbackTime", DateTime.Now.ToString("HH:mm:ss"));
  130. joLog.Add("OpterCode", Global.user.ID);
  131. joLog.Add("OpterName", Global.user.name);
  132. JObject joParam = new JObject();
  133. joParam.Add("LogData", joLog);
  134. joParam.Add("HospitalDr", Global.inf.hospitalDr);
  135. joParam.Add("InterfaceDr", Global.inf.interfaceDr);
  136. joParam.Add("UpdateUserID", Global.user.ID);
  137. string serviceCode = "09010148";
  138. string inpar = JsonHelper.setIrisInpar(serviceCode, joParam).ToString();
  139. JObject joRtn = invoker.invokeInsuService(inpar, "更新事前事中反馈日志");
  140. if (JsonHelper.parseIrisRtnValue(joRtn, out string updateErr) == 0)
  141. {
  142. successCount++;
  143. }
  144. else
  145. {
  146. Global.writeLog($"更新反馈日志失败(WarnRsltID={warnRsltId}): {updateErr}");
  147. }
  148. }
  149. Global.writeLog($"记录3103反馈日志: 成功{successCount}/{warnings.Count}");
  150. return successCount > 0 ? 0 : -1;
  151. }
  152. catch (Exception ex)
  153. {
  154. errMsg = $"记录反馈日志异常: {ex.Message}";
  155. Global.writeLog($"记录3103反馈日志异常: {ex.Message}");
  156. return -1;
  157. }
  158. }
  159. /// <summary>
  160. /// 查询违规日志(支持操作员、病人姓名、项目名称过滤)
  161. /// </summary>
  162. public System.Data.DataTable QueryLogs(string startDate, string endDate,
  163. string operatorName, string patientName, string itemName, out string errMsg)
  164. {
  165. errMsg = "";
  166. try
  167. {
  168. string sqlStr = $"SELECT * FROM SQLUser.BS_PreInProcess_ViolationLog where 1=1 and Interface_Dr = {Global.inf.interfaceDr}";
  169. sqlStr += $" and InfDate>='{startDate}' and InfDate<='{endDate}'";
  170. if (!string.IsNullOrEmpty(operatorName))
  171. {
  172. sqlStr += $" and OpterName = {operatorName}";
  173. }
  174. if (!string.IsNullOrEmpty(patientName))
  175. {
  176. sqlStr += $" and PatnName = {patientName}";
  177. }
  178. if (!string.IsNullOrEmpty(itemName))
  179. {
  180. sqlStr += $" and ItemName = {itemName}";
  181. }
  182. MIIrisServices mis= new MIIrisServices();
  183. JObject joRtn = mis.DynamicQuery(sqlStr, "查询");
  184. if (JsonHelper.parseIrisRtnValue(joRtn, out errMsg) != 0)
  185. {
  186. return null;
  187. }
  188. return joRtn["result"]?["data"]?.ToObject<System.Data.DataTable>();
  189. }
  190. catch (Exception ex)
  191. {
  192. errMsg = $"查询日志异常: {ex.Message}";
  193. return null;
  194. }
  195. }
  196. /// <summary>
  197. /// 按日期范围查询违规日志
  198. /// </summary>
  199. public System.Data.DataTable QueryLogsByDate(string startDate, string endDate, out string errMsg)
  200. {
  201. return QueryLogs(startDate, endDate, "", "", "", out errMsg);
  202. }
  203. #region 私有辅助方法
  204. /// <summary>
  205. /// 从输出数据中解析违规结果列表
  206. /// </summary>
  207. private List<JObject> ParseViolationList(JObject outputData)
  208. {
  209. List<JObject> list = new List<JObject>();
  210. if (outputData == null) return list;
  211. JToken jtResult = outputData["output"]["result"];
  212. if (jtResult is JArray jaResult)
  213. {
  214. foreach (JToken jt in jaResult)
  215. {
  216. if (jt is JObject jo) list.Add(jo);
  217. }
  218. }
  219. else if (jtResult is JObject joResult)
  220. {
  221. JArray jaJudgeResults = joResult["judge_result_dtos"] as JArray;
  222. if (jaJudgeResults != null && jaJudgeResults.Count > 0)
  223. {
  224. foreach (JToken jt in jaJudgeResults)
  225. {
  226. if (jt is JObject jo) list.Add(jo);
  227. }
  228. }
  229. else
  230. {
  231. list.Add(joResult);
  232. }
  233. }
  234. return list;
  235. }
  236. /// <summary>
  237. /// 构建单条违规宽表记录
  238. /// </summary>
  239. private JObject BuildViolationLog(JObject joVio, JArray jaPatientDtos, string taskId,
  240. string tradeCode, string tradeName, string triggerScene, string msgId,
  241. string opterCode, string opterName, DateTime infTime)
  242. {
  243. JObject joLog = new JObject();
  244. // 交易头部
  245. joLog.Add("TaskID", taskId);
  246. joLog.Add("TradeCode", tradeCode);
  247. joLog.Add("TriggerScene", triggerScene);
  248. joLog.Add("MsgID", msgId);
  249. joLog.Add("OpterCode", opterCode);
  250. joLog.Add("OpterName", opterName);
  251. joLog.Add("InfDate", infTime.ToString("yyyy-MM-dd"));
  252. joLog.Add("InfTime", infTime.ToString("HH:mm:ss"));
  253. // 违规信息
  254. string jrId = joVio["jr_id"]?.ToString() ?? "";
  255. string patnId = joVio["patn_id"]?.ToString() ?? "";
  256. string mdtrtId = joVio["mdtrt_id"]?.ToString() ?? "";
  257. joLog.Add("WarnType", joVio["warn_type"]?.ToString() ?? "");
  258. joLog.Add("JrID", jrId);
  259. joLog.Add("RuleID", joVio["rule_id"]?.ToString() ?? "");
  260. joLog.Add("RuleName", joVio["rule_name"]?.ToString() ?? "");
  261. joLog.Add("VolaCont", joVio["vola_cont"]?.ToString() ?? "");
  262. joLog.Add("VolaAmt", ParseDecimal(joVio["vola_amt"]));
  263. joLog.Add("SevDeg", joVio["sev_deg"]?.ToString() ?? "");
  264. joLog.Add("VolaBhvrType", joVio["vola_bhvr_type"]?.ToString() ?? "");
  265. joLog.Add("PatnID", patnId);
  266. joLog.Add("MdtrtID", mdtrtId);
  267. // 匹配患者与就诊信息
  268. JObject joPatient = FindPatientById(jaPatientDtos, patnId);
  269. if (joPatient != null)
  270. {
  271. joLog.Add("PatnName", joPatient["patn_name"]?.ToString() ?? "");
  272. joLog.Add("Gend", joPatient["gend"]?.ToString() ?? "");
  273. joLog.Add("Brdy", joPatient["brdy"]?.ToString() ?? "");
  274. JArray jaEncounters = joPatient["fsi_encounter_dtos"] as JArray;
  275. JObject joEncounter = FindEncounterById(jaEncounters, mdtrtId);
  276. if (joEncounter != null)
  277. {
  278. joLog.Add("DeptName", joEncounter["adm_dept_name"]?.ToString() ?? "");
  279. string medType = joEncounter["med_type"]?.ToString() ?? "";
  280. joLog.Add("MedType", medType);
  281. joLog.Add("MedTypeName", MedTypeToName(medType));
  282. // 项目信息:优先从违规明细匹配,其次取就诊的第一个医嘱
  283. FillItemInfo(joLog, joVio, joEncounter);
  284. }
  285. }
  286. // 确保项目字段有默认值
  287. EnsureDefault(joLog, "ItemCode", "");
  288. EnsureDefault(joLog, "ItemName", "");
  289. EnsureDefault(joLog, "Cnt", 0);
  290. EnsureDefault(joLog, "Pric", 0);
  291. EnsureDefault(joLog, "Sumamt", 0);
  292. return joLog;
  293. }
  294. /// <summary>
  295. /// 填充项目信息(违规明细 -> 医嘱匹配 -> 默认值)
  296. /// </summary>
  297. private void FillItemInfo(JObject joLog, JObject joVio, JObject joEncounter)
  298. {
  299. JArray jaOrders = joEncounter["fsi_order_dtos"] as JArray;
  300. JArray jaDetails = joVio["judge_result_detail_dtos"] as JArray;
  301. JObject joOrder = null;
  302. if (jaDetails != null && jaDetails.Count > 0)
  303. {
  304. JObject joDetail = jaDetails[0] as JObject;
  305. string detailRxId = joDetail["rx_id"]?.ToString() ?? "";
  306. if (!string.IsNullOrEmpty(detailRxId) && jaOrders != null)
  307. {
  308. joOrder = FindOrderByRxId(jaOrders, detailRxId);
  309. }
  310. // 若未匹配到order,先取明细中的项目信息
  311. if (joOrder == null)
  312. {
  313. joLog.Add("ItemCode", joDetail["hilist_code"]?.ToString() ?? joDetail["item_code"]?.ToString() ?? "");
  314. joLog.Add("ItemName", joDetail["hilist_name"]?.ToString() ?? joDetail["item_name"]?.ToString() ?? "");
  315. joLog.Add("Cnt", ParseDecimal(joDetail["cnt"]));
  316. joLog.Add("Pric", ParseDecimal(joDetail["pric"]));
  317. joLog.Add("Sumamt", ParseDecimal(joDetail["sumamt"]));
  318. }
  319. }
  320. // 从医嘱取项目信息
  321. if (joOrder == null && jaOrders != null && jaOrders.Count > 0)
  322. {
  323. joOrder = jaOrders[0] as JObject;
  324. }
  325. if (joOrder != null && !joLog.ContainsKey("ItemName"))
  326. {
  327. joLog.Add("ItemCode", joOrder["hilist_code"]?.ToString() ?? joOrder["hosplist_code"]?.ToString() ?? "");
  328. joLog.Add("ItemName", joOrder["hilist_name"]?.ToString() ?? joOrder["hosplist_name"]?.ToString() ?? "");
  329. joLog.Add("Cnt", ParseDecimal(joOrder["cnt"]));
  330. joLog.Add("Pric", ParseDecimal(joOrder["pric"]));
  331. joLog.Add("Sumamt", ParseDecimal(joOrder["sumamt"]));
  332. }
  333. }
  334. /// <summary>
  335. /// 根据参保人ID查找患者对象
  336. /// </summary>
  337. private JObject FindPatientById(JArray jaPatientDtos, string patnId)
  338. {
  339. if (jaPatientDtos == null || string.IsNullOrEmpty(patnId)) return null;
  340. foreach (JToken jt in jaPatientDtos)
  341. {
  342. if (jt is JObject jo && jo["patn_id"]?.ToString() == patnId)
  343. return jo;
  344. }
  345. // 若ID匹配不到,返回第一个作为兜底
  346. return jaPatientDtos.Count > 0 ? jaPatientDtos[0] as JObject : null;
  347. }
  348. /// <summary>
  349. /// 根据就诊ID查找就诊对象
  350. /// </summary>
  351. private JObject FindEncounterById(JArray jaEncounters, string mdtrtId)
  352. {
  353. if (jaEncounters == null || string.IsNullOrEmpty(mdtrtId)) return null;
  354. foreach (JToken jt in jaEncounters)
  355. {
  356. if (jt is JObject jo && jo["mdtrt_id"]?.ToString() == mdtrtId)
  357. return jo;
  358. }
  359. return jaEncounters.Count > 0 ? jaEncounters[0] as JObject : null;
  360. }
  361. /// <summary>
  362. /// 根据处方ID查找医嘱对象
  363. /// </summary>
  364. private JObject FindOrderByRxId(JArray jaOrders, string rxId)
  365. {
  366. if (jaOrders == null || string.IsNullOrEmpty(rxId)) return null;
  367. foreach (JToken jt in jaOrders)
  368. {
  369. if (jt is JObject jo && jo["rx_id"]?.ToString() == rxId)
  370. return jo;
  371. }
  372. return null;
  373. }
  374. /// <summary>
  375. /// 医疗类别编码转中文名称
  376. /// </summary>
  377. private string MedTypeToName(string medType)
  378. {
  379. switch (medType)
  380. {
  381. case "11": return "普通门诊";
  382. case "13": return "门诊慢特病";
  383. case "21": return "普通住院";
  384. case "22": return "住院分娩";
  385. case "24": return "急诊留观转住院";
  386. case "25": return "日间手术";
  387. case "51": return "生育门诊";
  388. case "52": return "生育住院";
  389. default: return medType;
  390. }
  391. }
  392. /// <summary>
  393. /// 安全解析数值
  394. /// </summary>
  395. private decimal ParseDecimal(JToken jtValue)
  396. {
  397. if (jtValue == null) return 0;
  398. if (decimal.TryParse(jtValue.ToString(), out decimal result))
  399. return result;
  400. return 0;
  401. }
  402. /// <summary>
  403. /// 确保字段存在默认值
  404. /// </summary>
  405. private void EnsureDefault(JObject jo, string key, object defaultValue)
  406. {
  407. if (!jo.ContainsKey(key))
  408. jo.Add(key, JToken.FromObject(defaultValue));
  409. }
  410. #endregion
  411. }
  412. }