ElectronicSettlementCertificate.cs 18 KB

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