ElectronicSettlementCertificate.cs 15 KB

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