JiangSuWorkInjuryBusiness.cs 68 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. using System.Threading;
  8. using Newtonsoft.Json;
  9. using Newtonsoft.Json.Linq;
  10. namespace ThCardReader
  11. {
  12. /// <summary>
  13. /// 江苏工伤联网结算业务类
  14. /// 基于JSSiInterface.dll动态库,严格按照江苏工伤联网接口规范v2.1实现
  15. /// 参考JiangSuSocialCardBusiness.cs的成功架构模式
  16. /// </summary>
  17. public class JiangSuWorkInjuryBusiness
  18. {
  19. #region DLL导入声明 - 严格按照工伤文档规范
  20. /// <summary>
  21. /// 初始化函数 - 检查整个运行环境
  22. /// </summary>
  23. /// <param name="pErrMsg">错误信息</param>
  24. /// <returns>成功:0,失败:-1</returns>
  25. [DllImport("JSSiInterface.dll", EntryPoint = "Si_INIT", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  26. private extern static int Si_INIT(StringBuilder pErrMsg);
  27. /// <summary>
  28. /// 交易函数 - 处理所有业务交易
  29. /// </summary>
  30. /// <param name="inputdata">输入参数JSON字符串</param>
  31. /// <param name="outputdata">输出参数JSON字符串</param>
  32. /// <returns>成功:0,失败:<0</returns>
  33. [DllImport("JSSiInterface.dll", EntryPoint = "Si_Busi", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  34. private extern static int Si_Busi(string inputdata, StringBuilder outputdata);
  35. #endregion
  36. #region 状态管理和内部变量
  37. /// <summary>
  38. /// 系统初始化状态
  39. /// </summary>
  40. private static bool isInitialized = false;
  41. /// <summary>
  42. /// 当前配置信息
  43. /// </summary>
  44. private static WorkInjuryConfig currentConfig = null;
  45. /// <summary>
  46. /// 当前签到流水号
  47. /// </summary>
  48. private static string currentSignNo = "";
  49. /// <summary>
  50. /// 签到时间
  51. /// </summary>
  52. private static DateTime signInTime = DateTime.MinValue;
  53. /// <summary>
  54. /// 签到操作员
  55. /// </summary>
  56. private static string signInOperator = "";
  57. /// <summary>
  58. /// 报文ID缓存(用于冲正交易)
  59. /// </summary>
  60. private static readonly Dictionary<string, TransactionRecord> transactionRecords = new Dictionary<string, TransactionRecord>();
  61. /// <summary>
  62. /// 序列号计数器(用于生成唯一报文ID)
  63. /// </summary>
  64. private static int sequenceCounter = 0;
  65. private static readonly object sequenceLock = new object();
  66. /// <summary>
  67. /// 交易记录类
  68. /// </summary>
  69. private class TransactionRecord
  70. {
  71. public string MessageId { get; set; }
  72. public string TransactionCode { get; set; }
  73. public DateTime TransactionTime { get; set; }
  74. public string OperatorId { get; set; }
  75. }
  76. #endregion
  77. #region 配置管理类
  78. /// <summary>
  79. /// 江苏工伤联网配置类 - 严格按照工伤文档规范定义
  80. /// </summary>
  81. public class WorkInjuryConfig
  82. {
  83. /// <summary>
  84. /// 协议机构编号 - 由江苏人社分配
  85. /// </summary>
  86. public string FixmedinsCode { get; set; } = "SQ201348";
  87. /// <summary>
  88. /// 协议机构名称
  89. /// </summary>
  90. public string FixmedinsName { get; set; } = "沭阳铭和医院";
  91. /// <summary>
  92. /// 接收方系统代码 - 默认"JSYTH"
  93. /// </summary>
  94. public string ReceiverSysCode { get; set; } = "JSYTH";
  95. /// <summary>
  96. /// 接口版本号 - 如"V2.1"
  97. /// </summary>
  98. public string InterfaceVersion { get; set; } = "V2.1";
  99. /// <summary>
  100. /// 经办人类别 - 1:经办人 2:自助终端 3:移动终端
  101. /// </summary>
  102. public string OperatorType { get; set; } = "1";
  103. /// <summary>
  104. /// 默认经办人编号
  105. /// </summary>
  106. public string DefaultOperator { get; set; } = "001";
  107. /// <summary>
  108. /// 默认经办人姓名
  109. /// </summary>
  110. public string DefaultOperatorName { get; set; } = "系统管理员";
  111. /// <summary>
  112. /// 日志路径
  113. /// </summary>
  114. public string LogPath { get; set; } = "logs/workinjury/";
  115. }
  116. /// <summary>
  117. /// 工伤联网交易请求参数 - 按照工伤文档标准格式
  118. /// </summary>
  119. public class WorkInjuryTransactionRequest
  120. {
  121. /// <summary>
  122. /// 交易编号 - 4位数字
  123. /// </summary>
  124. public string TransactionCode { get; set; }
  125. /// <summary>
  126. /// 业务参数 - 具体交易的输入参数
  127. /// </summary>
  128. public object BusinessParams { get; set; }
  129. /// <summary>
  130. /// 识别方式 - 1:实体社保卡 2:电子凭证
  131. /// </summary>
  132. public string IdentifyMode { get; set; } = "1";
  133. /// <summary>
  134. /// 电子社保卡二维码(识别方式为2时必填)
  135. /// </summary>
  136. public string QrCodeInfo { get; set; } = "";
  137. /// <summary>
  138. /// 经办人编号(可选,使用默认值)
  139. /// </summary>
  140. public string OperatorId { get; set; } = "";
  141. /// <summary>
  142. /// 经办人姓名(可选,使用默认值)
  143. /// </summary>
  144. public string OperatorName { get; set; } = "";
  145. }
  146. #endregion
  147. #region 交易代码映射表
  148. /// <summary>
  149. /// 交易代码映射表 - 基于工伤文档接口列表
  150. /// </summary>
  151. private static readonly Dictionary<string, string> TransactionMapping = new Dictionary<string, string>
  152. {
  153. // 认证类
  154. {"SignIn", "9001"}, // 签到
  155. {"SignOut", "9002"}, // 签退
  156. // 业务类 - 核心功能
  157. {"ReadCard", "1101"}, // 读卡
  158. {"RegisterPatient", "2201"}, // 门诊/住院登记
  159. {"CancelRegister", "2202"}, // 登记撤销
  160. {"ModifyRegister", "2203"}, // 登记信息修改
  161. {"UploadPrescription", "2204"}, // 处方明细上报
  162. {"CancelPrescription", "2205"}, // 处方明细撤销
  163. {"PreSettle", "2206"}, // 费用预结算
  164. {"Settle", "2207"}, // 费用结算
  165. {"CancelSettle", "2208"}, // 费用结算撤销
  166. {"ReverseTransaction", "2209"}, // 冲正交易
  167. // 体检类 - 体检协议机构使用
  168. {"QueryExamSchedule", "8101"}, // 查询体检排班信息
  169. {"UpdateExamSchedule", "8102"}, // 更新体检排班信息
  170. {"QueryExamReservation", "8103"}, // 查询体检预约信息
  171. {"ExamRegister", "8104"}, // 体检登记
  172. {"UploadExamDetail", "8105"}, // 上传体检明细
  173. {"QueryExamSettle", "8106"}, // 查询体检结算信息
  174. {"QueryExamDetail", "8107"}, // 查询体检明细
  175. {"ConfirmExamResult", "8108"}, // 体检结果确认
  176. {"QuerySupplementCard", "8109"}, // 补刷卡登记查询
  177. // 转院类
  178. {"UploadReferral", "2301"}, // 转诊转院申请信息上传
  179. {"QueryReferral", "2302"}, // 转诊转院申请信息查询
  180. {"CancelReferral", "2303"}, // 转诊转院申请信息撤销
  181. // 对账类
  182. {"TotalAccount", "1320"}, // 总额对账
  183. {"DetailAccount", "1321"}, // 明细对账
  184. // 下载类
  185. {"BatchDownload", "1301"}, // 批量数据下载
  186. {"QueryFeeDetail", "9103"}, // 费用明细详细信息下载
  187. {"QueryPrescriptionDetail", "9104"}, // 处方明细下载
  188. {"QueryRecentVisit", "9105"} // 参保人近期就诊信息查询
  189. };
  190. #endregion
  191. #region 核心业务方法 - 初始化函数
  192. /// <summary>
  193. /// 初始化江苏工伤联网系统
  194. /// 参考JiangSuSocialCardBusiness的Initialize方法,但适配工伤业务
  195. /// </summary>
  196. /// <param name="config">配置参数</param>
  197. /// <returns>初始化结果</returns>
  198. [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
  199. [System.Security.SecurityCritical]
  200. public static JObject Initialize(WorkInjuryConfig config = null)
  201. {
  202. var result = new JObject();
  203. try
  204. {
  205. // 添加调试信息
  206. result["debug_init_step"] = "开始初始化";
  207. // 使用默认配置或传入配置
  208. if (config == null)
  209. {
  210. config = new WorkInjuryConfig();
  211. }
  212. result["debug_init_step"] = "配置准备完成";
  213. // 检查DLL文件存在性
  214. var dllCheckResult = CheckDllExists();
  215. result["debug_dll_check"] = dllCheckResult;
  216. if (!(bool)dllCheckResult["success"])
  217. {
  218. return dllCheckResult;
  219. }
  220. result["debug_init_step"] = "DLL检查通过";
  221. // 确保日志目录存在
  222. if (!Directory.Exists(config.LogPath))
  223. {
  224. Directory.CreateDirectory(config.LogPath);
  225. }
  226. result["debug_init_step"] = "日志目录准备完成";
  227. // 调用底层初始化函数 - 增加缓冲区大小并添加调试信息
  228. StringBuilder errMsg = new StringBuilder(4096); // 增加缓冲区大小
  229. result["debug_init_step"] = "准备调用Si_INIT";
  230. // 添加DLL调用前的最后检查
  231. try
  232. {
  233. // 测试DLL是否可以加载(不调用函数)
  234. result["debug_init_step"] = "检查DLL加载状态";
  235. var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JSSiInterface.dll");
  236. if (!File.Exists(dllPath))
  237. {
  238. result["success"] = false;
  239. result["code"] = 2004;
  240. result["message"] = $"DLL文件不存在: {dllPath}";
  241. result["device"] = "江苏工伤联网接口";
  242. result["debug_init_step"] = "DLL文件不存在";
  243. return result;
  244. }
  245. result["debug_dll_path"] = dllPath;
  246. result["debug_dll_size"] = new FileInfo(dllPath).Length;
  247. result["debug_init_step"] = "DLL文件检查通过,准备调用Si_INIT";
  248. }
  249. catch (Exception dllCheckEx)
  250. {
  251. result["success"] = false;
  252. result["code"] = 2005;
  253. result["message"] = $"DLL文件检查异常: {dllCheckEx.Message}";
  254. result["device"] = "江苏工伤联网接口";
  255. result["debug_init_step"] = "DLL文件检查异常";
  256. result["debug_error"] = dllCheckEx.Message;
  257. return result;
  258. }
  259. int initResult;
  260. try
  261. {
  262. // 在调用前添加更多保护
  263. result["debug_init_step"] = "即将调用Si_INIT - 这里可能发生异常";
  264. // 设置一个标记,如果DLL调用导致进程崩溃,至少我们知道问题在这里
  265. var beforeCallTime = DateTime.Now;
  266. result["debug_si_init_call_time"] = beforeCallTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
  267. initResult = Si_INIT(errMsg);
  268. var afterCallTime = DateTime.Now;
  269. var callDuration = (afterCallTime - beforeCallTime).TotalMilliseconds;
  270. result["debug_init_step"] = "Si_INIT调用完成";
  271. result["debug_dll_result"] = initResult;
  272. result["debug_dll_errmsg"] = errMsg.ToString();
  273. result["debug_si_init_duration"] = $"{callDuration}ms";
  274. result["debug_si_init_return_time"] = afterCallTime.ToString("yyyy-MM-dd HH:mm:ss.fff");
  275. }
  276. catch (System.AccessViolationException avEx)
  277. {
  278. result["success"] = false;
  279. result["code"] = 2001;
  280. result["message"] = "DLL访问冲突异常 - 可能是DLL版本不匹配或依赖缺失";
  281. result["device"] = "江苏工伤联网接口";
  282. result["exception"] = "AccessViolationException";
  283. result["debug_init_step"] = "Si_INIT调用发生访问冲突";
  284. result["debug_error"] = avEx.Message;
  285. return result;
  286. }
  287. catch (System.DllNotFoundException dllEx)
  288. {
  289. result["success"] = false;
  290. result["code"] = 2002;
  291. result["message"] = "找不到JSSiInterface.dll或其依赖库";
  292. result["device"] = "江苏工伤联网接口";
  293. result["exception"] = "DllNotFoundException";
  294. result["debug_init_step"] = "Si_INIT调用时DLL未找到";
  295. result["debug_error"] = dllEx.Message;
  296. return result;
  297. }
  298. catch (System.EntryPointNotFoundException epEx)
  299. {
  300. result["success"] = false;
  301. result["code"] = 2003;
  302. result["message"] = "DLL中找不到Si_INIT函数入口点";
  303. result["device"] = "江苏工伤联网接口";
  304. result["exception"] = "EntryPointNotFoundException";
  305. result["debug_init_step"] = "Si_INIT函数入口点未找到";
  306. result["debug_error"] = epEx.Message;
  307. return result;
  308. }
  309. if (initResult == 0)
  310. {
  311. isInitialized = true;
  312. currentConfig = config;
  313. result["success"] = true;
  314. result["code"] = 200;
  315. result["message"] = "江苏工伤联网系统初始化成功";
  316. result["device"] = "江苏工伤联网接口";
  317. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  318. result["version"] = config.InterfaceVersion;
  319. result["dllErrorMsg"] = errMsg.ToString();
  320. result["debug_init_step"] = "初始化成功";
  321. result["config"] = new JObject
  322. {
  323. ["fixmedinsCode"] = config.FixmedinsCode,
  324. ["fixmedinsName"] = config.FixmedinsName,
  325. ["interfaceVersion"] = config.InterfaceVersion
  326. };
  327. LogInfo($"系统初始化成功,版本: {config.InterfaceVersion}");
  328. }
  329. else
  330. {
  331. string errorMsg = errMsg.ToString();
  332. result["success"] = false;
  333. result["code"] = 1000 + Math.Abs(initResult);
  334. result["message"] = $"江苏工伤联网系统初始化失败: {errorMsg}";
  335. result["device"] = "江苏工伤联网接口";
  336. result["errorCode"] = initResult;
  337. result["dllErrorMsg"] = errorMsg;
  338. result["debug_init_step"] = "初始化失败";
  339. LogError($"系统初始化失败,错误码: {initResult}, 错误信息: {errorMsg}");
  340. }
  341. }
  342. catch (Exception ex)
  343. {
  344. result["success"] = false;
  345. result["code"] = 1001;
  346. result["message"] = $"江苏工伤联网系统初始化异常: {ex.Message}";
  347. result["device"] = "江苏工伤联网接口";
  348. result["exception"] = ex.GetType().Name;
  349. result["debug_init_step"] = "捕获到异常";
  350. result["debug_exception_details"] = ex.ToString();
  351. LogError($"系统初始化异常: {ex}");
  352. }
  353. return result;
  354. }
  355. #endregion
  356. #region 核心业务方法 - 通用接口函数
  357. /// <summary>
  358. /// 通用工伤联网交易处理接口
  359. /// 支持所有工伤文档定义的交易类型
  360. /// </summary>
  361. /// <param name="transactionName">交易名称(可使用中文名称或交易编号)</param>
  362. /// <param name="businessParams">业务参数对象</param>
  363. /// <param name="identifyMode">识别方式:1-实体社保卡,2-电子凭证</param>
  364. /// <param name="qrCodeInfo">电子社保卡二维码(识别方式为2时必填)</param>
  365. /// <param name="operatorId">经办人编号(可选)</param>
  366. /// <param name="operatorName">经办人姓名(可选)</param>
  367. /// <returns>交易处理结果</returns>
  368. [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions]
  369. [System.Security.SecurityCritical]
  370. public static JObject ProcessWorkInjuryTransaction(
  371. string transactionName,
  372. object businessParams = null,
  373. string identifyMode = "1",
  374. string qrCodeInfo = "",
  375. string operatorId = "",
  376. string operatorName = "")
  377. {
  378. var result = new JObject();
  379. object inputData = null; // 新增变量,便于所有分支都能访问
  380. try
  381. {
  382. // 1. 解析交易编号
  383. string transactionCode = GetTransactionCode(transactionName);
  384. if (string.IsNullOrEmpty(transactionCode))
  385. {
  386. result["success"] = false;
  387. result["code"] = 1002;
  388. result["message"] = $"不支持的交易类型: {transactionName}";
  389. result["device"] = "江苏工伤联网接口";
  390. return result;
  391. }
  392. // 2. 检查初始化状态
  393. if (!isInitialized)
  394. {
  395. var autoInitResult = Initialize();
  396. if (!(bool)autoInitResult["success"])
  397. {
  398. result["success"] = false;
  399. result["code"] = 1003;
  400. result["message"] = "江苏工伤联网系统未初始化";
  401. result["device"] = "江苏工伤联网接口";
  402. result["autoInitError"] = autoInitResult["message"];
  403. return result;
  404. }
  405. }
  406. // // 3. 检查签到状态(除签到交易外)
  407. // if (transactionCode != "9001" && string.IsNullOrEmpty(currentSignNo))
  408. // {
  409. // var signInResult = ProcessWorkInjuryTransaction("SignIn");
  410. // if (!(bool)signInResult["success"])
  411. // {
  412. // result["success"] = false;
  413. // result["code"] = 1004;
  414. // result["message"] = "自动签到失败,无法进行业务交易";
  415. // result["device"] = "江苏工伤联网接口";
  416. // result["signInError"] = signInResult["message"];
  417. // return result;
  418. // }
  419. // }
  420. // === 自动签到功能(只在业务类交易且未带sign_no时自动签到) ===
  421. if (transactionCode != "9001" && transactionCode != "9002")
  422. {
  423. string signNoFromParams = null;
  424. if (businessParams is JObject bpObj && bpObj["sign_no"] != null)
  425. signNoFromParams = bpObj["sign_no"].ToString();
  426. else if (businessParams != null)
  427. {
  428. var prop = businessParams.GetType().GetProperty("sign_no");
  429. if (prop != null)
  430. signNoFromParams = prop.GetValue(businessParams)?.ToString();
  431. }
  432. if (string.IsNullOrEmpty(signNoFromParams))
  433. {
  434. var signInResult = ProcessWorkInjuryTransaction("SignIn");
  435. if (!(bool)signInResult["success"])
  436. {
  437. result["success"] = false;
  438. result["code"] = 1004;
  439. result["message"] = "自动签到失败,无法进行业务交易";
  440. result["device"] = "江苏工伤联网接口";
  441. result["signInError"] = signInResult["message"];
  442. return result;
  443. }
  444. string newSignNo = signInResult["data"]?["sign_no"]?.ToString() ?? "";
  445. if (businessParams is JObject bpObj2)
  446. bpObj2["sign_no"] = newSignNo;
  447. else if (businessParams != null)
  448. {
  449. var prop = businessParams.GetType().GetProperty("sign_no");
  450. if (prop != null && prop.CanWrite)
  451. prop.SetValue(businessParams, newSignNo);
  452. }
  453. }
  454. }
  455. // 4. 构造标准输入参数
  456. inputData = BuildTransactionInput(transactionCode, businessParams, identifyMode, qrCodeInfo, operatorId, operatorName);
  457. string inputJson = JsonConvert.SerializeObject(inputData, Formatting.None);
  458. // 5. 调用底层DLL交易函数 - 增强异常处理
  459. StringBuilder outputBuffer = new StringBuilder(40000);
  460. result["debug_input_json"] = inputJson;
  461. result["debug_transaction_step"] = "准备调用Si_Busi";
  462. int dllResult;
  463. try
  464. {
  465. dllResult = Si_Busi(inputJson, outputBuffer);
  466. result["debug_transaction_step"] = "Si_Busi调用完成";
  467. result["debug_dll_result"] = dllResult;
  468. }
  469. catch (System.AccessViolationException avEx)
  470. {
  471. result["success"] = false;
  472. result["code"] = 2011;
  473. result["message"] = $"交易{transactionName} DLL访问冲突异常";
  474. result["device"] = "江苏工伤联网接口";
  475. result["exception"] = "AccessViolationException";
  476. result["debug_transaction_step"] = "Si_Busi调用发生访问冲突";
  477. result["debug_error"] = avEx.Message;
  478. result["transactionCode"] = transactionCode;
  479. if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData);
  480. return result;
  481. }
  482. catch (System.DllNotFoundException dllEx)
  483. {
  484. result["success"] = false;
  485. result["code"] = 2012;
  486. result["message"] = $"交易{transactionName} 找不到JSSiInterface.dll";
  487. result["device"] = "江苏工伤联网接口";
  488. result["exception"] = "DllNotFoundException";
  489. result["debug_transaction_step"] = "Si_Busi调用时DLL未找到";
  490. result["debug_error"] = dllEx.Message;
  491. result["transactionCode"] = transactionCode;
  492. if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData);
  493. return result;
  494. }
  495. catch (System.EntryPointNotFoundException epEx)
  496. {
  497. result["success"] = false;
  498. result["code"] = 2013;
  499. result["message"] = $"交易{transactionName} DLL中找不到Si_Busi函数入口点";
  500. result["device"] = "江苏工伤联网接口";
  501. result["exception"] = "EntryPointNotFoundException";
  502. result["debug_transaction_step"] = "Si_Busi函数入口点未找到";
  503. result["debug_error"] = epEx.Message;
  504. result["transactionCode"] = transactionCode;
  505. if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData);
  506. return result;
  507. }
  508. // 6. 解析和处理返回结果
  509. string outputJson = outputBuffer.ToString();
  510. result["debug_output_json"] = outputJson;
  511. result = ParseTransactionOutput(outputJson, dllResult, transactionCode);
  512. // 7. 特殊交易后处理
  513. PostProcessTransaction(transactionCode, result);
  514. if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData);
  515. LogInfo($"交易 {transactionName}({transactionCode}) 完成,结果: {result["success"]}");
  516. }
  517. catch (Exception ex)
  518. {
  519. result["success"] = false;
  520. result["code"] = 1005;
  521. result["message"] = $"工伤联网交易异常: {ex.Message}";
  522. result["device"] = "江苏工伤联网接口";
  523. result["exception"] = ex.GetType().Name;
  524. result["transactionName"] = transactionName;
  525. if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData);
  526. LogError($"交易 {transactionName} 异常: {ex.Message}");
  527. }
  528. return result;
  529. }
  530. #endregion
  531. #region 输入输出处理方法
  532. /// <summary>
  533. /// 构造标准的工伤联网交易输入参数
  534. /// 严格按照工伤文档表2格式构造,支持特殊交易格式
  535. /// </summary>
  536. private static object BuildTransactionInput(string transactionCode, object businessParams,
  537. string identifyMode, string qrCodeInfo, string operatorId, string operatorName)
  538. {
  539. // 生成唯一的发送方报文ID:协议机构编号(6)+时间(14)+顺序号(4)
  540. string msgId = GenerateMessageId();
  541. // 使用配置的经办人信息或传入的参数
  542. string finalOperatorId = string.IsNullOrEmpty(operatorId) ? currentConfig.DefaultOperator : operatorId;
  543. string finalOperatorName = string.IsNullOrEmpty(operatorName) ? currentConfig.DefaultOperatorName : operatorName;
  544. // 构造基础参数对象
  545. var baseInputData = new
  546. {
  547. infno = transactionCode, // 交易编号
  548. msgid = msgId, // 发送方报文ID
  549. recer_sys_code = currentConfig.ReceiverSysCode, // 接收方系统代码
  550. infver = currentConfig.InterfaceVersion, // 接口版本号
  551. opter_type = currentConfig.OperatorType, // 经办人类别
  552. opter = finalOperatorId, // 经办人
  553. opter_name = finalOperatorName, // 经办人姓名
  554. inf_time = DateTime.Now.ToString("yyyyMMddHHmmss"), // 交易时间
  555. fixmedins_code = currentConfig.FixmedinsCode, // 协议机构编号
  556. fixmedins_name = currentConfig.FixmedinsName, // 协议机构名称
  557. sign_no = transactionCode == "9001" ? "" : currentSignNo, // 签到流水号
  558. idfi_mode = identifyMode, // 识别方式
  559. qrcode_info = qrCodeInfo // 电子社保卡二维码
  560. };
  561. // 处理特殊交易格式
  562. object finalInputData = BuildSpecialTransactionInput(baseInputData, transactionCode, businessParams);
  563. // 保存发送方报文ID用于可能的冲正操作
  564. SaveMessageId(msgId, transactionCode);
  565. return finalInputData;
  566. }
  567. /// <summary>
  568. /// 处理特殊交易的输入格式
  569. /// 按照工伤文档要求,某些交易需要特殊的参数格式
  570. /// </summary>
  571. private static object BuildSpecialTransactionInput(object baseInputData, string transactionCode, object businessParams)
  572. {
  573. switch (transactionCode)
  574. {
  575. case "2204": // 处方明细上报 - 使用feedetail代替input
  576. return new
  577. {
  578. // 复制基础参数
  579. infno = ((dynamic)baseInputData).infno,
  580. msgid = ((dynamic)baseInputData).msgid,
  581. recer_sys_code = ((dynamic)baseInputData).recer_sys_code,
  582. infver = ((dynamic)baseInputData).infver,
  583. opter_type = ((dynamic)baseInputData).opter_type,
  584. opter = ((dynamic)baseInputData).opter,
  585. opter_name = ((dynamic)baseInputData).opter_name,
  586. inf_time = ((dynamic)baseInputData).inf_time,
  587. fixmedins_code = ((dynamic)baseInputData).fixmedins_code,
  588. fixmedins_name = ((dynamic)baseInputData).fixmedins_name,
  589. sign_no = ((dynamic)baseInputData).sign_no,
  590. idfi_mode = ((dynamic)baseInputData).idfi_mode,
  591. qrcode_info = ((dynamic)baseInputData).qrcode_info,
  592. // 特殊格式:使用feedetail替代input
  593. feedetail = businessParams ?? new object[] { }
  594. };
  595. case "8105": // 上传体检明细 - 使用tjfeedetail代替input
  596. return new
  597. {
  598. // 复制基础参数
  599. infno = ((dynamic)baseInputData).infno,
  600. msgid = ((dynamic)baseInputData).msgid,
  601. recer_sys_code = ((dynamic)baseInputData).recer_sys_code,
  602. infver = ((dynamic)baseInputData).infver,
  603. opter_type = ((dynamic)baseInputData).opter_type,
  604. opter = ((dynamic)baseInputData).opter,
  605. opter_name = ((dynamic)baseInputData).opter_name,
  606. inf_time = ((dynamic)baseInputData).inf_time,
  607. fixmedins_code = ((dynamic)baseInputData).fixmedins_code,
  608. fixmedins_name = ((dynamic)baseInputData).fixmedins_name,
  609. sign_no = ((dynamic)baseInputData).sign_no,
  610. idfi_mode = ((dynamic)baseInputData).idfi_mode,
  611. qrcode_info = ((dynamic)baseInputData).qrcode_info,
  612. // 特殊格式:使用tjfeedetail替代input
  613. tjfeedetail = businessParams ?? new object[] { }
  614. };
  615. default: // 标准交易格式
  616. return new
  617. {
  618. // 复制基础参数
  619. infno = ((dynamic)baseInputData).infno,
  620. msgid = ((dynamic)baseInputData).msgid,
  621. recer_sys_code = ((dynamic)baseInputData).recer_sys_code,
  622. infver = ((dynamic)baseInputData).infver,
  623. opter_type = ((dynamic)baseInputData).opter_type,
  624. opter = ((dynamic)baseInputData).opter,
  625. opter_name = ((dynamic)baseInputData).opter_name,
  626. inf_time = ((dynamic)baseInputData).inf_time,
  627. fixmedins_code = ((dynamic)baseInputData).fixmedins_code,
  628. fixmedins_name = ((dynamic)baseInputData).fixmedins_name,
  629. sign_no = ((dynamic)baseInputData).sign_no,
  630. idfi_mode = ((dynamic)baseInputData).idfi_mode,
  631. qrcode_info = ((dynamic)baseInputData).qrcode_info,
  632. // 标准格式:使用input
  633. input = businessParams ?? new { }
  634. };
  635. }
  636. }
  637. /// <summary>
  638. /// 解析交易输出结果并进行错误处理
  639. /// </summary>
  640. private static JObject ParseTransactionOutput(string outputJson, int dllResult, string transactionCode)
  641. {
  642. var result = new JObject();
  643. try
  644. {
  645. // DLL层面错误
  646. if (dllResult != 0)
  647. {
  648. result["success"] = false;
  649. result["code"] = 2000 + Math.Abs(dllResult);
  650. result["message"] = $"DLL调用失败,错误码: {dllResult}";
  651. result["device"] = "江苏工伤联网接口";
  652. result["dllErrorCode"] = dllResult;
  653. result["transactionCode"] = transactionCode;
  654. result["rawOutput"] = outputJson;
  655. return result;
  656. }
  657. // 解析JSON输出
  658. if (string.IsNullOrEmpty(outputJson))
  659. {
  660. result["success"] = false;
  661. result["code"] = 2001;
  662. result["message"] = "交易返回数据为空";
  663. return result;
  664. }
  665. var outputData = JObject.Parse(outputJson);
  666. // 业务层面错误检查
  667. string infcode = outputData["infcode"]?.ToString() ?? "";
  668. string errMsg = outputData["err_msg"]?.ToString() ?? "";
  669. string warnMsg = outputData["warn_msg"]?.ToString() ?? "";
  670. if (infcode == "0")
  671. {
  672. // 交易成功
  673. result["success"] = true;
  674. result["code"] = 200;
  675. result["message"] = string.IsNullOrEmpty(warnMsg) ? "交易成功" : warnMsg;
  676. // 返回完整的输出对象,包含 infcode/err_msg/warn_msg/output 等
  677. result["data"] = outputData;
  678. result["transactionCode"] = transactionCode;
  679. result["infRefMsgId"] = outputData["inf_refmsgid"];
  680. result["refMsgTime"] = outputData["refmsg_time"];
  681. result["respondTime"] = outputData["respond_time"];
  682. result["warnMsg"] = warnMsg;
  683. }
  684. else
  685. {
  686. // 业务失败
  687. result["success"] = false;
  688. result["code"] = 3000 + Math.Abs(int.Parse(infcode));
  689. result["message"] = string.IsNullOrEmpty(errMsg) ? "交易失败" : errMsg;
  690. result["transactionCode"] = transactionCode;
  691. result["businessErrorCode"] = infcode;
  692. result["businessErrorMsg"] = errMsg;
  693. }
  694. // 保存完整的原始返回数据
  695. result["rawOutput"] = outputJson;
  696. }
  697. catch (JsonException jsonEx)
  698. {
  699. result["success"] = false;
  700. result["code"] = 2002;
  701. result["message"] = $"返回数据解析失败: {jsonEx.Message}";
  702. result["rawOutput"] = outputJson;
  703. }
  704. catch (Exception ex)
  705. {
  706. result["success"] = false;
  707. result["code"] = 2003;
  708. result["message"] = $"输出解析异常: {ex.Message}";
  709. result["exception"] = ex.GetType().Name;
  710. }
  711. return result;
  712. }
  713. /// <summary>
  714. /// 特殊交易后处理逻辑
  715. /// </summary>
  716. private static void PostProcessTransaction(string transactionCode, JObject result)
  717. {
  718. if ((bool)result["success"])
  719. {
  720. switch (transactionCode)
  721. {
  722. case "9001": // 签到交易成功
  723. var signData = result["data"];
  724. if (signData != null)
  725. {
  726. currentSignNo = signData["sign_no"]?.ToString() ?? "";
  727. // 从服务器返回数据中获取签到时间,格式:yyyyMMddHHmmss
  728. string signTimeStr = signData["sign_time"]?.ToString() ?? "";
  729. if (!string.IsNullOrEmpty(signTimeStr) && signTimeStr.Length == 14)
  730. {
  731. try
  732. {
  733. signInTime = DateTime.ParseExact(signTimeStr, "yyyyMMddHHmmss", null);
  734. }
  735. catch
  736. {
  737. // 如果解析失败,使用本地时间作为备选
  738. signInTime = DateTime.Now;
  739. LogError($"服务器返回的签到时间格式错误: {signTimeStr},使用本地时间");
  740. }
  741. }
  742. else
  743. {
  744. // 如果服务器没有返回签到时间,使用本地时间
  745. signInTime = DateTime.Now;
  746. LogInfo($"服务器未返回签到时间,使用本地时间: {signInTime:yyyy-MM-dd HH:mm:ss}");
  747. }
  748. signInOperator = currentConfig?.DefaultOperator ?? "";
  749. // 记录签到成功日志
  750. LogInfo($"签到成功,流水号: {currentSignNo}, 时间: {signInTime:yyyy-MM-dd HH:mm:ss}");
  751. }
  752. break;
  753. case "9002": // 签退交易成功
  754. // 清除签到状态
  755. currentSignNo = "";
  756. signInTime = DateTime.MinValue;
  757. signInOperator = "";
  758. // 记录签退时间,使用统一格式
  759. string signOutTimeStr = DateTime.Now.ToString("yyyyMMddHHmmss");
  760. LogInfo($"签退成功,时间: {signOutTimeStr}");
  761. break;
  762. }
  763. }
  764. }
  765. #endregion
  766. #region 辅助工具方法
  767. /// <summary>
  768. /// 获取交易编号
  769. /// 支持中文名称、英文名称和直接的数字编号
  770. /// </summary>
  771. private static string GetTransactionCode(string transactionName)
  772. {
  773. if (string.IsNullOrEmpty(transactionName))
  774. return "";
  775. // 如果直接是4位数字编号,直接返回
  776. if (transactionName.Length == 4 && transactionName.All(char.IsDigit))
  777. return transactionName;
  778. // 从映射表中查找
  779. if (TransactionMapping.ContainsKey(transactionName))
  780. return TransactionMapping[transactionName];
  781. // 支持中文名称映射
  782. var chineseMapping = new Dictionary<string, string>
  783. {
  784. {"签到", "9001"}, {"签退", "9002"},
  785. {"读卡", "1101"}, {"登记", "2201"}, {"结算", "2207"},
  786. {"撤销", "2208"}, {"冲正", "2209"}, {"预结算", "2206"},
  787. {"处方上报", "2204"}, {"处方撤销", "2205"},
  788. {"总额对账", "1320"}, {"明细对账", "1321"},
  789. {"批量下载", "1301"}
  790. };
  791. if (chineseMapping.ContainsKey(transactionName))
  792. return chineseMapping[transactionName];
  793. return "";
  794. }
  795. /// <summary>
  796. /// 生成唯一的发送方报文ID
  797. /// 格式:协议机构编号(6位) + 时间戳(14位) + 序列号(4位) = 24位
  798. /// </summary>
  799. private static string GenerateMessageId()
  800. {
  801. lock (sequenceLock)
  802. {
  803. string institutionCode = currentConfig?.FixmedinsCode ?? "SQ201348";
  804. if (institutionCode.Length > 6)
  805. institutionCode = institutionCode.Substring(0, 6);
  806. else
  807. institutionCode = institutionCode.PadRight(6, '0');
  808. string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
  809. sequenceCounter = (sequenceCounter + 1) % 10000; // 保持4位数字
  810. string sequence = sequenceCounter.ToString("D4");
  811. return institutionCode + timestamp + sequence;
  812. }
  813. }
  814. /// <summary>
  815. /// 保存发送方报文ID用于冲正交易
  816. /// </summary>
  817. private static void SaveMessageId(string msgId, string transactionCode)
  818. {
  819. var record = new TransactionRecord
  820. {
  821. MessageId = msgId,
  822. TransactionCode = transactionCode,
  823. TransactionTime = DateTime.Now,
  824. OperatorId = currentConfig?.DefaultOperator ?? ""
  825. };
  826. transactionRecords[msgId] = record;
  827. // 清理过期记录(7天前的)
  828. var expireTime = DateTime.Now.AddDays(-7);
  829. var expiredKeys = transactionRecords
  830. .Where(kv => kv.Value.TransactionTime < expireTime)
  831. .Select(kv => kv.Key)
  832. .ToList();
  833. foreach (var key in expiredKeys)
  834. {
  835. transactionRecords.Remove(key);
  836. }
  837. }
  838. /// <summary>
  839. /// 确保DLL文件在正确位置(参考医保代码的成功实现)
  840. /// </summary>
  841. private static JObject CheckDllExists()
  842. {
  843. var result = new JObject();
  844. try
  845. {
  846. string programDir = AppDomain.CurrentDomain.BaseDirectory;
  847. string targetDllPath = Path.Combine(programDir, "JSSiInterface.dll");
  848. // 如果目标位置已经有DLL文件,直接返回成功
  849. if (File.Exists(targetDllPath))
  850. {
  851. FileInfo fileInfo = new FileInfo(targetDllPath);
  852. result["success"] = true;
  853. result["code"] = 200;
  854. result["message"] = "JSSiInterface.dll文件检查通过";
  855. result["device"] = "江苏工伤联网接口";
  856. result["data"] = new JObject
  857. {
  858. ["dllPath"] = targetDllPath,
  859. ["fileSize"] = fileInfo.Length,
  860. ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss"),
  861. ["programDirectory"] = programDir
  862. };
  863. return result;
  864. }
  865. // 如果目标位置没有DLL,尝试从其他位置查找并复制(参考医保代码)
  866. string[] possiblePaths = {
  867. Path.Combine(programDir, "bin", "x86", "Release", "JSSiInterface.dll"),
  868. Path.Combine(programDir, "bin", "Debug", "JSSiInterface.dll"),
  869. Path.Combine(programDir, "bin", "Release", "JSSiInterface.dll"),
  870. Path.Combine(programDir, "ThCardReader", "bin", "x86", "Release", "JSSiInterface.dll"),
  871. Path.Combine(programDir, "ThCardReader", "bin", "Debug", "JSSiInterface.dll"),
  872. Path.Combine(programDir, "..", "ThCardReader", "bin", "x86", "Release", "JSSiInterface.dll")
  873. };
  874. string sourcePath = null;
  875. foreach (string path in possiblePaths)
  876. {
  877. if (File.Exists(path))
  878. {
  879. sourcePath = path;
  880. break;
  881. }
  882. }
  883. if (sourcePath != null)
  884. {
  885. // 复制DLL文件到程序目录
  886. File.Copy(sourcePath, targetDllPath, true);
  887. FileInfo fileInfo = new FileInfo(targetDllPath);
  888. result["success"] = true;
  889. result["code"] = 200;
  890. result["message"] = "JSSiInterface.dll文件已复制到位";
  891. result["device"] = "江苏工伤联网接口";
  892. result["data"] = new JObject
  893. {
  894. ["sourcePath"] = sourcePath,
  895. ["targetPath"] = targetDllPath,
  896. ["fileSize"] = fileInfo.Length,
  897. ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")
  898. };
  899. }
  900. else
  901. {
  902. result["success"] = false;
  903. result["code"] = 8001;
  904. result["message"] = "找不到JSSiInterface.dll文件";
  905. result["device"] = "江苏工伤联网接口";
  906. result["data"] = new JObject
  907. {
  908. ["searchedPaths"] = string.Join("; ", possiblePaths),
  909. ["programDirectory"] = programDir,
  910. ["suggestion"] = "请确保JSSiInterface.dll文件在程序目录或bin/x86/Release目录中"
  911. };
  912. }
  913. }
  914. catch (Exception ex)
  915. {
  916. result["success"] = false;
  917. result["code"] = 8002;
  918. result["message"] = $"DLL文件检查异常: {ex.Message}";
  919. result["device"] = "江苏工伤联网接口";
  920. result["exception"] = ex.GetType().Name;
  921. }
  922. return result;
  923. }
  924. /// <summary>
  925. /// 记录信息日志
  926. /// </summary>
  927. private static void LogInfo(string message)
  928. {
  929. try
  930. {
  931. if (currentConfig != null)
  932. {
  933. string logPath = Path.Combine(currentConfig.LogPath, $"workinjury_{DateTime.Now:yyyyMMdd}.log");
  934. string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [INFO] {message}{Environment.NewLine}";
  935. File.AppendAllText(logPath, logEntry);
  936. }
  937. }
  938. catch
  939. {
  940. // 日志记录失败不影响主要业务
  941. }
  942. }
  943. /// <summary>
  944. /// 记录错误日志
  945. /// </summary>
  946. private static void LogError(string message)
  947. {
  948. try
  949. {
  950. if (currentConfig != null)
  951. {
  952. string logPath = Path.Combine(currentConfig.LogPath, $"workinjury_{DateTime.Now:yyyyMMdd}.log");
  953. string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [ERROR] {message}{Environment.NewLine}";
  954. File.AppendAllText(logPath, logEntry);
  955. }
  956. }
  957. catch
  958. {
  959. // 日志记录失败不影响主要业务
  960. }
  961. }
  962. #endregion
  963. #region 便民方法集合 - 按照设计文档要求
  964. /// <summary>
  965. /// 批量上传处方明细
  966. /// 自动处理大量处方的分批上传(每批最多50条)
  967. /// </summary>
  968. public static JObject BatchUploadPrescriptions(object[] prescriptions, string patientId = "", string visitNo = "")
  969. {
  970. var result = new JObject();
  971. var results = new JArray();
  972. try
  973. {
  974. if (prescriptions == null || prescriptions.Length == 0)
  975. {
  976. result["success"] = false;
  977. result["code"] = 1006;
  978. result["message"] = "处方明细不能为空";
  979. return result;
  980. }
  981. // 分批处理(每批50条)
  982. const int batchSize = 50;
  983. int totalBatches = (int)Math.Ceiling((double)prescriptions.Length / batchSize);
  984. int successCount = 0;
  985. int failureCount = 0;
  986. for (int i = 0; i < totalBatches; i++)
  987. {
  988. var batch = prescriptions.Skip(i * batchSize).Take(batchSize).ToArray();
  989. var batchParams = new
  990. {
  991. patient_id = patientId,
  992. visit_no = visitNo,
  993. batch_no = (i + 1).ToString(),
  994. prescriptions = batch
  995. };
  996. var batchResult = ProcessWorkInjuryTransaction("UploadPrescription", batchParams);
  997. if ((bool)batchResult["success"])
  998. {
  999. successCount++;
  1000. }
  1001. else
  1002. {
  1003. failureCount++;
  1004. }
  1005. results.Add(new JObject
  1006. {
  1007. ["batchNo"] = i + 1,
  1008. ["itemCount"] = batch.Length,
  1009. ["success"] = batchResult["success"],
  1010. ["message"] = batchResult["message"],
  1011. ["code"] = batchResult["code"]
  1012. });
  1013. }
  1014. result["success"] = failureCount == 0;
  1015. result["code"] = failureCount == 0 ? 200 : 1007;
  1016. result["message"] = $"批量上传完成,成功{successCount}批,失败{failureCount}批";
  1017. result["data"] = new JObject
  1018. {
  1019. ["totalBatches"] = totalBatches,
  1020. ["totalItems"] = prescriptions.Length,
  1021. ["successCount"] = successCount,
  1022. ["failureCount"] = failureCount,
  1023. ["batchResults"] = results
  1024. };
  1025. }
  1026. catch (Exception ex)
  1027. {
  1028. result["success"] = false;
  1029. result["code"] = 1008;
  1030. result["message"] = $"批量上传异常: {ex.Message}";
  1031. result["exception"] = ex.GetType().Name;
  1032. }
  1033. return result;
  1034. }
  1035. /// <summary>
  1036. /// 完整的工伤就医流程
  1037. /// 自动完成:读卡 -> 登记 -> 预结算 -> 结算 的完整流程
  1038. /// </summary>
  1039. public static JObject CompleteWorkInjuryProcess(object patientInfo = null, object[] feeDetails = null)
  1040. {
  1041. var result = new JObject();
  1042. var processSteps = new JArray();
  1043. try
  1044. {
  1045. // 第1步:读卡
  1046. var readCardResult = ProcessWorkInjuryTransaction("ReadCard");
  1047. processSteps.Add(new JObject
  1048. {
  1049. ["step"] = 1,
  1050. ["name"] = "读卡",
  1051. ["success"] = readCardResult["success"],
  1052. ["message"] = readCardResult["message"],
  1053. ["data"] = readCardResult["data"]
  1054. });
  1055. if (!(bool)readCardResult["success"])
  1056. {
  1057. result["success"] = false;
  1058. result["message"] = "读卡失败,流程终止";
  1059. result["processSteps"] = processSteps;
  1060. return result;
  1061. }
  1062. // 第2步:登记
  1063. var registerParams = patientInfo ?? new
  1064. {
  1065. visit_type = "1", // 门诊
  1066. dept_code = "001",
  1067. dept_name = "内科",
  1068. doctor_code = "DOC001",
  1069. doctor_name = "张医生"
  1070. };
  1071. var registerResult = ProcessWorkInjuryTransaction("RegisterPatient", registerParams);
  1072. processSteps.Add(new JObject
  1073. {
  1074. ["step"] = 2,
  1075. ["name"] = "登记",
  1076. ["success"] = registerResult["success"],
  1077. ["message"] = registerResult["message"],
  1078. ["data"] = registerResult["data"]
  1079. });
  1080. if (!(bool)registerResult["success"])
  1081. {
  1082. result["success"] = false;
  1083. result["message"] = "登记失败,流程终止";
  1084. result["processSteps"] = processSteps;
  1085. return result;
  1086. }
  1087. // 如果有费用明细,进行预结算和结算
  1088. if (feeDetails != null && feeDetails.Length > 0)
  1089. {
  1090. // 第3步:预结算
  1091. var preSettleParams = new
  1092. {
  1093. visit_no = registerResult["data"]?["visit_no"]?.ToString() ?? "",
  1094. fee_details = feeDetails
  1095. };
  1096. var preSettleResult = ProcessWorkInjuryTransaction("PreSettle", preSettleParams);
  1097. processSteps.Add(new JObject
  1098. {
  1099. ["step"] = 3,
  1100. ["name"] = "预结算",
  1101. ["success"] = preSettleResult["success"],
  1102. ["message"] = preSettleResult["message"],
  1103. ["data"] = preSettleResult["data"]
  1104. });
  1105. if ((bool)preSettleResult["success"])
  1106. {
  1107. // 第4步:正式结算
  1108. var settleParams = new
  1109. {
  1110. visit_no = registerResult["data"]?["visit_no"]?.ToString() ?? "",
  1111. pre_settle_id = preSettleResult["data"]?["pre_settle_id"]?.ToString() ?? ""
  1112. };
  1113. var settleResult = ProcessWorkInjuryTransaction("Settle", settleParams);
  1114. processSteps.Add(new JObject
  1115. {
  1116. ["step"] = 4,
  1117. ["name"] = "结算",
  1118. ["success"] = settleResult["success"],
  1119. ["message"] = settleResult["message"],
  1120. ["data"] = settleResult["data"]
  1121. });
  1122. }
  1123. }
  1124. result["success"] = true;
  1125. result["code"] = 200;
  1126. result["message"] = "工伤就医流程完成";
  1127. result["processSteps"] = processSteps;
  1128. }
  1129. catch (Exception ex)
  1130. {
  1131. result["success"] = false;
  1132. result["code"] = 1009;
  1133. result["message"] = $"工伤就医流程异常: {ex.Message}";
  1134. result["exception"] = ex.GetType().Name;
  1135. result["processSteps"] = processSteps;
  1136. }
  1137. return result;
  1138. }
  1139. /// <summary>
  1140. /// 智能重试交易
  1141. /// 自动重试失败的交易,使用指数退避算法
  1142. /// </summary>
  1143. public static JObject SmartRetryTransaction(string transactionName, object businessParams = null,
  1144. int maxRetries = 3, int baseDelayMs = 1000)
  1145. {
  1146. var result = new JObject();
  1147. var retryAttempts = new JArray();
  1148. for (int attempt = 1; attempt <= maxRetries; attempt++)
  1149. {
  1150. var attemptResult = ProcessWorkInjuryTransaction(transactionName, businessParams);
  1151. retryAttempts.Add(new JObject
  1152. {
  1153. ["attempt"] = attempt,
  1154. ["success"] = attemptResult["success"],
  1155. ["message"] = attemptResult["message"],
  1156. ["code"] = attemptResult["code"],
  1157. ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
  1158. });
  1159. if ((bool)attemptResult["success"])
  1160. {
  1161. result = attemptResult; // 成功时返回最后一次的结果
  1162. result["retryInfo"] = new JObject
  1163. {
  1164. ["totalAttempts"] = attempt,
  1165. ["success"] = true,
  1166. ["retryAttempts"] = retryAttempts
  1167. };
  1168. return result;
  1169. }
  1170. // 如果不是最后一次尝试,等待后重试
  1171. if (attempt < maxRetries)
  1172. {
  1173. int delay = baseDelayMs * (int)Math.Pow(2, attempt - 1); // 指数退避
  1174. Thread.Sleep(delay);
  1175. }
  1176. }
  1177. // 所有重试都失败了
  1178. result["success"] = false;
  1179. result["code"] = 1010;
  1180. result["message"] = $"交易{transactionName}经过{maxRetries}次重试后仍然失败";
  1181. result["retryInfo"] = new JObject
  1182. {
  1183. ["totalAttempts"] = maxRetries,
  1184. ["success"] = false,
  1185. ["retryAttempts"] = retryAttempts
  1186. };
  1187. return result;
  1188. }
  1189. /// <summary>
  1190. /// 获取交易统计信息
  1191. /// 提供系统性能和健康状况监控
  1192. /// </summary>
  1193. public static JObject GetTransactionStatistics()
  1194. {
  1195. var result = new JObject();
  1196. try
  1197. {
  1198. var stats = new JObject
  1199. {
  1200. ["currentTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  1201. ["systemStatus"] = new JObject
  1202. {
  1203. ["initialized"] = isInitialized,
  1204. ["signedIn"] = !string.IsNullOrEmpty(currentSignNo),
  1205. ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss"),
  1206. ["currentSignNo"] = currentSignNo,
  1207. ["signInOperator"] = signInOperator
  1208. },
  1209. ["transactionRecords"] = new JObject
  1210. {
  1211. ["totalCount"] = transactionRecords.Count,
  1212. ["oldestRecord"] = transactionRecords.Count > 0 ?
  1213. transactionRecords.Values.Min(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss") : null,
  1214. ["newestRecord"] = transactionRecords.Count > 0 ?
  1215. transactionRecords.Values.Max(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss") : null
  1216. },
  1217. ["configuration"] = currentConfig != null ? new JObject
  1218. {
  1219. ["fixmedinsCode"] = currentConfig.FixmedinsCode,
  1220. ["fixmedinsName"] = currentConfig.FixmedinsName,
  1221. ["interfaceVersion"] = currentConfig.InterfaceVersion,
  1222. ["logPath"] = currentConfig.LogPath
  1223. } : null
  1224. };
  1225. result["success"] = true;
  1226. result["code"] = 200;
  1227. result["message"] = "统计信息获取成功";
  1228. result["data"] = stats;
  1229. }
  1230. catch (Exception ex)
  1231. {
  1232. result["success"] = false;
  1233. result["code"] = 1011;
  1234. result["message"] = $"获取统计信息异常: {ex.Message}";
  1235. result["exception"] = ex.GetType().Name;
  1236. }
  1237. return result;
  1238. }
  1239. /// <summary>
  1240. /// 系统健康检查
  1241. /// 全面检查系统状态和连接性
  1242. /// </summary>
  1243. public static JObject HealthCheck()
  1244. {
  1245. var result = new JObject();
  1246. var checks = new JObject();
  1247. try
  1248. {
  1249. // 1. 检查DLL文件
  1250. var dllCheck = CheckDllExists();
  1251. checks["dllStatus"] = new JObject
  1252. {
  1253. ["success"] = dllCheck["success"],
  1254. ["message"] = dllCheck["message"]
  1255. };
  1256. // 2. 检查初始化状态
  1257. checks["initStatus"] = new JObject
  1258. {
  1259. ["initialized"] = isInitialized,
  1260. ["hasConfig"] = currentConfig != null
  1261. };
  1262. // 3. 检查签到状态
  1263. checks["signInStatus"] = new JObject
  1264. {
  1265. ["signedIn"] = !string.IsNullOrEmpty(currentSignNo),
  1266. ["signNo"] = currentSignNo,
  1267. ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss")
  1268. };
  1269. // 4. 尝试调用系统函数(如果已初始化)
  1270. if (isInitialized)
  1271. {
  1272. var testResult = ProcessWorkInjuryTransaction("SignIn");
  1273. checks["connectionTest"] = new JObject
  1274. {
  1275. ["success"] = testResult["success"],
  1276. ["message"] = testResult["message"]
  1277. };
  1278. }
  1279. else
  1280. {
  1281. checks["connectionTest"] = new JObject
  1282. {
  1283. ["success"] = false,
  1284. ["message"] = "系统未初始化,无法测试连接"
  1285. };
  1286. }
  1287. // 5. 检查日志目录
  1288. if (currentConfig != null)
  1289. {
  1290. checks["logStatus"] = new JObject
  1291. {
  1292. ["pathExists"] = Directory.Exists(currentConfig.LogPath),
  1293. ["logPath"] = currentConfig.LogPath,
  1294. ["writable"] = true // 简化检查,实际可以尝试写入测试
  1295. };
  1296. }
  1297. // 综合评估健康状态
  1298. bool overallHealth = (bool)checks["dllStatus"]["success"] &&
  1299. (bool)checks["initStatus"]["initialized"];
  1300. result["success"] = true;
  1301. result["code"] = 200;
  1302. result["message"] = overallHealth ? "系统健康状态良好" : "系统存在问题";
  1303. result["data"] = new JObject
  1304. {
  1305. ["overallHealth"] = overallHealth,
  1306. ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  1307. ["checks"] = checks
  1308. };
  1309. }
  1310. catch (Exception ex)
  1311. {
  1312. result["success"] = false;
  1313. result["code"] = 1012;
  1314. result["message"] = $"健康检查异常: {ex.Message}";
  1315. result["exception"] = ex.GetType().Name;
  1316. }
  1317. return result;
  1318. }
  1319. /// <summary>
  1320. /// 获取当前签到状态
  1321. /// </summary>
  1322. public static JObject GetSignInStatus()
  1323. {
  1324. var result = new JObject();
  1325. try
  1326. {
  1327. result["success"] = true;
  1328. result["code"] = 200;
  1329. result["message"] = "签到状态获取成功";
  1330. result["data"] = new JObject
  1331. {
  1332. ["signedIn"] = !string.IsNullOrEmpty(currentSignNo),
  1333. ["signNo"] = currentSignNo,
  1334. ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss"),
  1335. ["signInOperator"] = signInOperator,
  1336. ["sessionDuration"] = signInTime == DateTime.MinValue ? null :
  1337. ((int)(DateTime.Now - signInTime).TotalMinutes).ToString() + "分钟"
  1338. };
  1339. }
  1340. catch (Exception ex)
  1341. {
  1342. result["success"] = false;
  1343. result["code"] = 1013;
  1344. result["message"] = $"获取签到状态异常: {ex.Message}";
  1345. result["exception"] = ex.GetType().Name;
  1346. }
  1347. return result;
  1348. }
  1349. /// <summary>
  1350. /// 根据交易记录进行冲正操作
  1351. /// </summary>
  1352. public static JObject ReverseTransactionByRecord(string originalMessageId)
  1353. {
  1354. var result = new JObject();
  1355. try
  1356. {
  1357. if (!transactionRecords.ContainsKey(originalMessageId))
  1358. {
  1359. result["success"] = false;
  1360. result["code"] = 1014;
  1361. result["message"] = $"找不到原交易记录: {originalMessageId}";
  1362. return result;
  1363. }
  1364. var originalRecord = transactionRecords[originalMessageId];
  1365. var reverseParams = new
  1366. {
  1367. original_msg_id = originalMessageId,
  1368. original_transaction_code = originalRecord.TransactionCode,
  1369. original_transaction_time = originalRecord.TransactionTime.ToString("yyyyMMddHHmmss"),
  1370. reverse_reason = "系统冲正"
  1371. };
  1372. result = ProcessWorkInjuryTransaction("ReverseTransaction", reverseParams);
  1373. if ((bool)result["success"])
  1374. {
  1375. // 冲正成功后,可以选择移除原记录
  1376. // transactionRecords.Remove(originalMessageId);
  1377. LogInfo($"冲正交易成功,原交易ID: {originalMessageId}");
  1378. }
  1379. }
  1380. catch (Exception ex)
  1381. {
  1382. result["success"] = false;
  1383. result["code"] = 1015;
  1384. result["message"] = $"冲正交易异常: {ex.Message}";
  1385. result["exception"] = ex.GetType().Name;
  1386. }
  1387. return result;
  1388. }
  1389. #endregion
  1390. }
  1391. }