ElectronicSettlementCertificate.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. using Newtonsoft.Json.Linq;
  2. using PTMedicalInsurance.Helper;
  3. using PTMedicalInsurance.Variables;
  4. using System;
  5. using System.IO;
  6. using System.IO.Compression;
  7. using System.Net.Http;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using System.Xml.Linq;
  11. namespace PTMedicalInsurance.Business
  12. {
  13. public class ElectronicSettlementCertificate
  14. {
  15. public string ecSettlCertNo;
  16. public string upldBchno;
  17. private string xmlPath;
  18. private string folderPath;
  19. private string xmlName;
  20. private string pdfName;
  21. private string savePath; //保存路径
  22. private InvokeHelper invoker = new InvokeHelper();
  23. private MIIrisServices mIs = new MIIrisServices();
  24. public ElectronicSettlementCertificate()
  25. {
  26. }
  27. // 路径属性
  28. public string SavePath
  29. {
  30. get { return savePath; } // 获取
  31. set
  32. {
  33. if (string.IsNullOrEmpty(value))
  34. {
  35. throw new ArgumentException("路径不能为空", nameof(value));
  36. }
  37. savePath = value; // 设置
  38. if (!Directory.Exists(savePath))
  39. {
  40. Directory.CreateDirectory(savePath);
  41. }
  42. }
  43. }
  44. #region 获取入参
  45. public JObject Get4901Input(JObject joIn)
  46. {
  47. //JoIn包含 患者数电号
  48. JObject joRtn = invoker.invokeHISService(JsonHelper.setIrisInpar("05110041", joIn).ToString(), "获取该数电号对应的数电入参"); //测试服05110039
  49. return joRtn;
  50. }
  51. #endregion
  52. #region 解构入参
  53. /// <summary>
  54. /// 异步下载,会造成压缩时文件还没生成,导致压缩包内容与实际文件夹内容不符
  55. /// </summary>
  56. /// <param name="url"></param>
  57. /// <param name="filePath"></param>
  58. /// <returns></returns>
  59. private async Task<bool> DownloadPdfFileAsync(string url, string filePath)
  60. {
  61. try
  62. {
  63. using (var httpClient = new HttpClient())
  64. {
  65. // 发送GET请求获取内容
  66. HttpResponseMessage response = await httpClient.GetAsync(url);
  67. if (response.IsSuccessStatusCode)
  68. {
  69. // 读取内容到字节数组
  70. byte[] fileBytes = await response.Content.ReadAsByteArrayAsync();
  71. // 将字节数组写入文件
  72. File.WriteAllBytes(filePath, fileBytes);
  73. return true;
  74. }
  75. else
  76. {
  77. Console.WriteLine($"无法下载文件,状态码:{response.StatusCode}");
  78. return false;
  79. }
  80. }
  81. }
  82. catch (Exception ex)
  83. {
  84. Console.WriteLine($"下载过程中发生错误:{ex.Message}");
  85. return false;
  86. }
  87. }
  88. /// <summary>
  89. /// 同步下载
  90. /// </summary>
  91. /// <param name="url"></param>
  92. /// <param name="filePath"></param>
  93. /// <returns></returns>
  94. private bool DownloadPdfFile(string url, string filePath)
  95. {
  96. try
  97. {
  98. using (var httpClient = new HttpClient())
  99. {
  100. // 发送GET请求获取内容
  101. HttpResponseMessage response = httpClient.GetAsync(url).Result;
  102. if (response.IsSuccessStatusCode)
  103. {
  104. // 读取内容到字节数组
  105. byte[] fileBytes = response.Content.ReadAsByteArrayAsync().Result;
  106. // 将字节数组写入文件
  107. File.WriteAllBytes(filePath, fileBytes);
  108. return true;
  109. }
  110. else
  111. {
  112. Console.WriteLine($"无法下载文件,状态码:{response.StatusCode}");
  113. return false;
  114. }
  115. }
  116. }
  117. catch (Exception ex)
  118. {
  119. Console.WriteLine($"下载过程中发生错误:{ex.Message}");
  120. return false;
  121. }
  122. }
  123. /// <summary>
  124. /// 保存XML文件
  125. /// </summary>
  126. /// <param name="jaXML"></param>
  127. private void SaveXML(JArray jaXML)
  128. {
  129. foreach (JObject jo in jaXML)
  130. {
  131. //税务数电票结构化数据文件(xml),命名规则为:电子结算凭证号码-extinfo ,因可能存在多条结算信息, 命名规则为:电子结算凭证号码-结算ID-extinfo
  132. ecSettlCertNo = JsonHelper.getDestValue(jo, "tktextinfo.billInfo.elecSetlCertNo");
  133. string setlId = JsonHelper.getDestValue(jo, "tktextinfo.mdtrtInfo.setlId");
  134. //xmlName = $"{ecSettlCertNo}-{setlId}-extinfo.xml";
  135. xmlName = $"{ecSettlCertNo}-extinfo.xml";
  136. pdfName = $"{ecSettlCertNo}.pdf";
  137. // 创建临时XML文件
  138. folderPath = $@"{savePath}\ElecXml\{ecSettlCertNo}";
  139. xmlPath = $@"{savePath}\ElecXml\{ecSettlCertNo}\{xmlName}";
  140. if (!Directory.Exists(folderPath))
  141. {
  142. Directory.CreateDirectory(folderPath);
  143. }
  144. XmlHelper xmler = new XmlHelper();
  145. string xmlContent = xmler.ToXML(jo.ToString());
  146. XElement xe = XElement.Parse(xmlContent);
  147. string xml = xmler.setInput(xe);
  148. File.WriteAllText(xmlPath, xml, Encoding.UTF8);
  149. }
  150. }
  151. /// <summary>
  152. /// 压缩指定的文件夹及其内容到ZIP文件中。
  153. /// </summary>
  154. /// <param name="sourceFolderPath">源文件夹路径。</param>
  155. /// <param name="zipFilePath">输出的ZIP文件路径。</param>
  156. private void CompressFolderToZIP(string sourceFolderPath, string zipFilePath)
  157. {
  158. if (!Directory.Exists(sourceFolderPath))
  159. {
  160. throw new DirectoryNotFoundException($"源文件夹不存在: {sourceFolderPath}");
  161. }
  162. try
  163. {
  164. using (FileStream fileStream = new FileStream(zipFilePath, FileMode.Create))
  165. {
  166. using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true))
  167. {
  168. //AddFolderToZip(archive, sourceFolderPath, Path.GetFileName(sourceFolderPath));
  169. AddFolderToZip(archive, sourceFolderPath, ""); //相对路径,为空则表示ZIP无第一层
  170. }
  171. }
  172. }
  173. catch (Exception ex)
  174. {
  175. throw new Exception($"压缩文件夹时发生错误: {ex.Message}");
  176. }
  177. }
  178. /// <summary>
  179. /// 递归地将文件夹添加到ZIP存档中。
  180. /// </summary>
  181. /// <param name="archive">ZIP存档对象。</param>
  182. /// <param name="folderPath">当前处理的文件夹路径。</param>
  183. /// <param name="basePath">基础路径,用于构建ZIP内的相对路径。</param>
  184. private static void AddFolderToZip(ZipArchive archive, string folderPath, string basePath)
  185. {
  186. DirectoryInfo directoryInfo = new DirectoryInfo(folderPath);
  187. // 添加当前文件夹中的所有文件到ZIP存档
  188. foreach (FileInfo fileInfo in directoryInfo.GetFiles())
  189. {
  190. string entryPath = Path.Combine(basePath, fileInfo.Name);
  191. ZipArchiveEntry entry = archive.CreateEntry(entryPath);
  192. using (FileStream stream = fileInfo.OpenRead())
  193. {
  194. using (Stream entryStream = entry.Open())
  195. {
  196. stream.CopyTo(entryStream);
  197. }
  198. }
  199. }
  200. // 递归地处理所有子文件夹
  201. foreach (DirectoryInfo subDirInfo in directoryInfo.GetDirectories())
  202. {
  203. string subFolderPath = Path.Combine(folderPath, subDirInfo.Name);
  204. string subBasePath = Path.Combine(basePath, subDirInfo.Name);
  205. AddFolderToZip(archive, subFolderPath, subBasePath);
  206. }
  207. }
  208. /// <summary>
  209. /// 转换位Base64
  210. /// </summary>
  211. /// <param name="path"></param>
  212. /// <returns></returns>
  213. private string ZipTobase64(string path)
  214. {
  215. byte[] zipBytes = File.ReadAllBytes(path);
  216. return Convert.ToBase64String(zipBytes);
  217. }
  218. private string FormatToFiveDigits(int seconds)
  219. {
  220. // 确保秒数不会超过五位数的最大值
  221. if (seconds > 86400)
  222. throw new ArgumentOutOfRangeException("秒", "一天的秒数不能超过86400");
  223. // 使用格式化字符串将秒数转换为五位数的字符串
  224. return seconds.ToString("D5");
  225. }
  226. /// <summary>
  227. /// 根据当天0点计算当前时刻的5位时间戳,然后加上当前日期,组成序号。要求每个上传时间需要超过1秒,否则会有重复问题
  228. /// </summary>
  229. /// <returns></returns>
  230. private string GetSeqNoBySecondStamp()
  231. {
  232. // 获取当天零点的时间
  233. DateTime todayMidnight = new DateTime(DateTime.Today.Year, DateTime.Today.Month, DateTime.Today.Day);
  234. // 计算从当天零点到现在的时间差(秒)
  235. TimeSpan timeSinceMidnight = DateTime.Now - todayMidnight;
  236. int secondsSinceMidnight = (int)timeSinceMidnight.TotalSeconds;
  237. // 将秒数转换为5位时间戳
  238. return DateTime.Now.ToString("yyyyMMdd") + FormatToFiveDigits(secondsSinceMidnight); //2024102168281
  239. //return DateTime.Now.ToString("yyyyMMdd") + "68282"; // 68281
  240. }
  241. /// <summary>
  242. /// 正式解构HIS返回的入参,包括医保编码转换,PDF下载,XML文件保存,ZIP压缩,zip转Base64,业务编号命名等
  243. /// </summary>
  244. /// <param name="joSource"></param>
  245. /// <returns></returns>
  246. public JObject Convert4901Input(JObject joSource)
  247. {
  248. //调用医保服务开始转换为医保对应的编码 09010127
  249. JObject joConvertRtn = mIs.convertEcSettlUploadInpar(joSource);
  250. JObject joData = JObject.Parse(JsonHelper.getDestValue(joConvertRtn,"data"));
  251. //指定PDF,xml文件,xml文件夹路径
  252. //保存xml ,保存过程中给路径等私有变量赋值
  253. SaveXML(JArray.Parse(JsonHelper.getDestValue(joData, "xml")));
  254. //保存PDF
  255. string pdfUrl = JsonHelper.getDestValue(joData, "pdfUrl");
  256. //DownloadPdfFileAsync(pdfUrl,$@"{folderPath}\{pdfName}");
  257. DownloadPdfFile(pdfUrl, $@"{folderPath}\{pdfName}");
  258. //压缩文件为ZIP,并保存到文件夹同级
  259. CompressFolderToZIP(folderPath, $@"{savePath}\ElecXml\{ecSettlCertNo}.zip");
  260. //转换ZIP为Base64
  261. string base64 = ZipTobase64($@"{savePath}\ElecXml\{ecSettlCertNo}.zip");
  262. joData["ftfileCompac"] = base64;
  263. joData["filename"] = $@"{ecSettlCertNo}.zip";
  264. joData["elecSetlCertCnt"] = int.Parse(joData["elecSetlCertCnt"].ToString()) + 1;
  265. upldBchno = GetSeqNoBySecondStamp();
  266. joData["upldBchno"] = upldBchno;//上传日期年月日加5位顺序号
  267. //移除PDF和xml节点
  268. joData.Remove("xml");
  269. joData.Remove("pdfUrl");
  270. return joData;
  271. }
  272. #endregion
  273. #region 查询,反写数据库
  274. /// <summary>
  275. /// 查询患者电子发票
  276. /// </summary>
  277. /// <param name="joIn"></param>
  278. /// <returns></returns>
  279. public JObject QueryEcSettlCertList(JObject joIn)
  280. {
  281. //JoIn包含 起止时间,医院ID,接口ID,患者姓名,患者数电号
  282. JObject joHisRtn = invoker.invokeHISService(JsonHelper.setIrisInpar("05110040", joIn).ToString(), "查询某时间段内或某患者电子结算凭证清单"); //测试服为05110038
  283. string errMsg;
  284. if (JsonHelper.parseIrisRtnValue(joHisRtn, out errMsg) != 0)
  285. {
  286. return joHisRtn;
  287. }
  288. JArray jaData = JArray.Parse(JsonHelper.getDestValue(joHisRtn,"data"));
  289. JObject joInsuRtn = invoker.invokeInsuService(JsonHelper.setIrisInpar("09010128", jaData).ToString(), "根据传入的电子凭证记录匹配上传记录表");
  290. return joInsuRtn;
  291. }
  292. /// <summary>
  293. /// 查询患者电子发票
  294. /// </summary>
  295. /// <param name="joIn"></param>
  296. /// <returns></returns>
  297. public JObject MatchUploadRecord(JObject joIn)
  298. {
  299. //JoIn包含 起止时间,医院ID,接口ID,患者姓名,患者数电号
  300. JObject joRtn = invoker.invokeHISService(JsonHelper.setIrisInpar("05110040", joIn).ToString(), "查询某时间段内或某患者电子结算凭证清单");
  301. return joRtn;
  302. }
  303. /// <summary>
  304. /// 成功后更新记录表
  305. /// </summary>
  306. /// <param name="joIn"></param>
  307. /// <returns></returns>
  308. public JObject Update(JObject joIn)
  309. {
  310. //JoIn包含 起止时间,医院ID,接口ID,患者姓名,患者数电号
  311. JObject joInTmp = JsonHelper.setIrisInpar("02020007", joIn);
  312. joInTmp["session"][0]["userID"] = "166";
  313. joInTmp["session"][0]["hospID"] = Global.inf.hospitalDr;
  314. JObject joRtn = invoker.invokeInsuService(joInTmp.ToString(),"更新通用记录表");
  315. return joRtn;
  316. }
  317. #endregion
  318. #region 具体功能点封装
  319. /// <summary>
  320. /// 上传
  321. /// </summary>
  322. /// <param name="joIn"></param>
  323. /// <returns></returns>
  324. public JObject Upload(JObject joIn)
  325. {
  326. JObject joRtn = invoker.invokeCenterService("4901", JsonHelper.setCenterInpar("4901",joIn));
  327. return joRtn;
  328. }
  329. /// <summary>
  330. /// 重新上传
  331. /// </summary>
  332. /// <param name="joIn"></param>
  333. /// <returns></returns>
  334. public JObject ReUpload(JObject joIn)
  335. {
  336. JObject joRtn = invoker.invokeCenterService("4905", JsonHelper.setCenterInpar("4905", joIn));
  337. return joRtn;
  338. }
  339. /// <summary>
  340. /// 查询上传结果
  341. /// </summary>
  342. /// <param name="joIn"></param>
  343. /// <returns></returns>
  344. public JObject QueryUploadResult(JObject joIn)
  345. {
  346. JObject joRtn = invoker.invokeCenterService("4902", JsonHelper.setCenterInpar("4902", joIn));
  347. return joRtn;
  348. }
  349. /// <summary>
  350. /// 查询电子凭证上传状态
  351. /// </summary>
  352. /// <param name="joIn"></param>
  353. /// <returns></returns>
  354. public JObject QueryUploadStatus(JObject joIn)
  355. {
  356. JObject joRtn = invoker.invokeCenterService("5501", JsonHelper.setCenterInpar("5501", joIn));
  357. return joRtn;
  358. }
  359. #endregion
  360. #region 具体流程封装
  361. #endregion
  362. }
  363. }