JiangSuWorkInjuryBusiness.cs 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337
  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)]
  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)]
  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. public static JObject Initialize(WorkInjuryConfig config = null)
  199. {
  200. var result = new JObject();
  201. try
  202. {
  203. // 使用默认配置或传入配置
  204. if (config == null)
  205. {
  206. config = new WorkInjuryConfig();
  207. }
  208. // 检查DLL文件存在性
  209. var dllCheckResult = CheckDllExists();
  210. if (!(bool)dllCheckResult["success"])
  211. {
  212. return dllCheckResult;
  213. }
  214. // 确保日志目录存在
  215. if (!Directory.Exists(config.LogPath))
  216. {
  217. Directory.CreateDirectory(config.LogPath);
  218. }
  219. // 调用底层初始化函数
  220. StringBuilder errMsg = new StringBuilder(1024);
  221. int initResult = Si_INIT(errMsg);
  222. if (initResult == 0)
  223. {
  224. isInitialized = true;
  225. currentConfig = config;
  226. result["success"] = true;
  227. result["code"] = 200;
  228. result["message"] = "江苏工伤联网系统初始化成功";
  229. result["device"] = "江苏工伤联网接口";
  230. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  231. result["version"] = config.InterfaceVersion;
  232. result["dllErrorMsg"] = errMsg.ToString();
  233. LogInfo($"系统初始化成功,版本: {config.InterfaceVersion}");
  234. }
  235. else
  236. {
  237. string errorMsg = errMsg.ToString();
  238. result["success"] = false;
  239. result["code"] = 1000 + Math.Abs(initResult);
  240. result["message"] = $"江苏工伤联网系统初始化失败: {errorMsg}";
  241. result["device"] = "江苏工伤联网接口";
  242. result["errorCode"] = initResult;
  243. result["dllErrorMsg"] = errorMsg;
  244. LogError($"系统初始化失败,错误码: {initResult}, 错误信息: {errorMsg}");
  245. }
  246. }
  247. catch (Exception ex)
  248. {
  249. result["success"] = false;
  250. result["code"] = 1001;
  251. result["message"] = $"江苏工伤联网系统初始化异常: {ex.Message}";
  252. result["device"] = "江苏工伤联网接口";
  253. result["exception"] = ex.GetType().Name;
  254. LogError($"系统初始化异常: {ex.Message}");
  255. }
  256. return result;
  257. }
  258. #endregion
  259. #region 核心业务方法 - 通用接口函数
  260. /// <summary>
  261. /// 通用工伤联网交易处理接口
  262. /// 支持所有工伤文档定义的交易类型
  263. /// </summary>
  264. /// <param name="transactionName">交易名称(可使用中文名称或交易编号)</param>
  265. /// <param name="businessParams">业务参数对象</param>
  266. /// <param name="identifyMode">识别方式:1-实体社保卡,2-电子凭证</param>
  267. /// <param name="qrCodeInfo">电子社保卡二维码(识别方式为2时必填)</param>
  268. /// <param name="operatorId">经办人编号(可选)</param>
  269. /// <param name="operatorName">经办人姓名(可选)</param>
  270. /// <returns>交易处理结果</returns>
  271. public static JObject ProcessWorkInjuryTransaction(
  272. string transactionName,
  273. object businessParams = null,
  274. string identifyMode = "1",
  275. string qrCodeInfo = "",
  276. string operatorId = "",
  277. string operatorName = "")
  278. {
  279. var result = new JObject();
  280. try
  281. {
  282. // 1. 解析交易编号
  283. string transactionCode = GetTransactionCode(transactionName);
  284. if (string.IsNullOrEmpty(transactionCode))
  285. {
  286. result["success"] = false;
  287. result["code"] = 1002;
  288. result["message"] = $"不支持的交易类型: {transactionName}";
  289. result["device"] = "江苏工伤联网接口";
  290. return result;
  291. }
  292. // 2. 检查初始化状态
  293. if (!isInitialized)
  294. {
  295. var autoInitResult = Initialize();
  296. if (!(bool)autoInitResult["success"])
  297. {
  298. result["success"] = false;
  299. result["code"] = 1003;
  300. result["message"] = "江苏工伤联网系统未初始化";
  301. result["device"] = "江苏工伤联网接口";
  302. result["autoInitError"] = autoInitResult["message"];
  303. return result;
  304. }
  305. }
  306. // 3. 检查签到状态(除签到交易外)
  307. if (transactionCode != "9001" && string.IsNullOrEmpty(currentSignNo))
  308. {
  309. var signInResult = ProcessWorkInjuryTransaction("SignIn");
  310. if (!(bool)signInResult["success"])
  311. {
  312. result["success"] = false;
  313. result["code"] = 1004;
  314. result["message"] = "自动签到失败,无法进行业务交易";
  315. result["device"] = "江苏工伤联网接口";
  316. result["signInError"] = signInResult["message"];
  317. return result;
  318. }
  319. }
  320. // 4. 构造标准输入参数
  321. var inputData = BuildTransactionInput(transactionCode, businessParams, identifyMode, qrCodeInfo, operatorId, operatorName);
  322. string inputJson = JsonConvert.SerializeObject(inputData, Formatting.None);
  323. // 5. 调用底层DLL交易函数
  324. StringBuilder outputBuffer = new StringBuilder(40000);
  325. int dllResult = Si_Busi(inputJson, outputBuffer);
  326. // 6. 解析和处理返回结果
  327. string outputJson = outputBuffer.ToString();
  328. result = ParseTransactionOutput(outputJson, dllResult, transactionCode);
  329. // 7. 特殊交易后处理
  330. PostProcessTransaction(transactionCode, result);
  331. LogInfo($"交易 {transactionName}({transactionCode}) 完成,结果: {result["success"]}");
  332. }
  333. catch (Exception ex)
  334. {
  335. result["success"] = false;
  336. result["code"] = 1005;
  337. result["message"] = $"工伤联网交易异常: {ex.Message}";
  338. result["device"] = "江苏工伤联网接口";
  339. result["exception"] = ex.GetType().Name;
  340. result["transactionName"] = transactionName;
  341. LogError($"交易 {transactionName} 异常: {ex.Message}");
  342. }
  343. return result;
  344. }
  345. #endregion
  346. #region 输入输出处理方法
  347. /// <summary>
  348. /// 构造标准的工伤联网交易输入参数
  349. /// 严格按照工伤文档表2格式构造,支持特殊交易格式
  350. /// </summary>
  351. private static object BuildTransactionInput(string transactionCode, object businessParams,
  352. string identifyMode, string qrCodeInfo, string operatorId, string operatorName)
  353. {
  354. // 生成唯一的发送方报文ID:协议机构编号(6)+时间(14)+顺序号(4)
  355. string msgId = GenerateMessageId();
  356. // 使用配置的经办人信息或传入的参数
  357. string finalOperatorId = string.IsNullOrEmpty(operatorId) ? currentConfig.DefaultOperator : operatorId;
  358. string finalOperatorName = string.IsNullOrEmpty(operatorName) ? currentConfig.DefaultOperatorName : operatorName;
  359. // 构造基础参数对象
  360. var baseInputData = new
  361. {
  362. infno = transactionCode, // 交易编号
  363. msgid = msgId, // 发送方报文ID
  364. recer_sys_code = currentConfig.ReceiverSysCode, // 接收方系统代码
  365. infver = currentConfig.InterfaceVersion, // 接口版本号
  366. opter_type = currentConfig.OperatorType, // 经办人类别
  367. opter = finalOperatorId, // 经办人
  368. opter_name = finalOperatorName, // 经办人姓名
  369. inf_time = DateTime.Now.ToString("yyyyMMddHHmmss"), // 交易时间
  370. fixmedins_code = currentConfig.FixmedinsCode, // 协议机构编号
  371. fixmedins_name = currentConfig.FixmedinsName, // 协议机构名称
  372. sign_no = transactionCode == "9001" ? "" : currentSignNo, // 签到流水号
  373. idfi_mode = identifyMode, // 识别方式
  374. qrcode_info = qrCodeInfo // 电子社保卡二维码
  375. };
  376. // 处理特殊交易格式
  377. object finalInputData = BuildSpecialTransactionInput(baseInputData, transactionCode, businessParams);
  378. // 保存发送方报文ID用于可能的冲正操作
  379. SaveMessageId(msgId, transactionCode);
  380. return finalInputData;
  381. }
  382. /// <summary>
  383. /// 处理特殊交易的输入格式
  384. /// 按照工伤文档要求,某些交易需要特殊的参数格式
  385. /// </summary>
  386. private static object BuildSpecialTransactionInput(object baseInputData, string transactionCode, object businessParams)
  387. {
  388. switch (transactionCode)
  389. {
  390. case "2204": // 处方明细上报 - 使用feedetail代替input
  391. return new
  392. {
  393. // 复制基础参数
  394. infno = ((dynamic)baseInputData).infno,
  395. msgid = ((dynamic)baseInputData).msgid,
  396. recer_sys_code = ((dynamic)baseInputData).recer_sys_code,
  397. infver = ((dynamic)baseInputData).infver,
  398. opter_type = ((dynamic)baseInputData).opter_type,
  399. opter = ((dynamic)baseInputData).opter,
  400. opter_name = ((dynamic)baseInputData).opter_name,
  401. inf_time = ((dynamic)baseInputData).inf_time,
  402. fixmedins_code = ((dynamic)baseInputData).fixmedins_code,
  403. fixmedins_name = ((dynamic)baseInputData).fixmedins_name,
  404. sign_no = ((dynamic)baseInputData).sign_no,
  405. idfi_mode = ((dynamic)baseInputData).idfi_mode,
  406. qrcode_info = ((dynamic)baseInputData).qrcode_info,
  407. // 特殊格式:使用feedetail替代input
  408. feedetail = businessParams ?? new object[] { }
  409. };
  410. case "8105": // 上传体检明细 - 使用tjfeedetail代替input
  411. return new
  412. {
  413. // 复制基础参数
  414. infno = ((dynamic)baseInputData).infno,
  415. msgid = ((dynamic)baseInputData).msgid,
  416. recer_sys_code = ((dynamic)baseInputData).recer_sys_code,
  417. infver = ((dynamic)baseInputData).infver,
  418. opter_type = ((dynamic)baseInputData).opter_type,
  419. opter = ((dynamic)baseInputData).opter,
  420. opter_name = ((dynamic)baseInputData).opter_name,
  421. inf_time = ((dynamic)baseInputData).inf_time,
  422. fixmedins_code = ((dynamic)baseInputData).fixmedins_code,
  423. fixmedins_name = ((dynamic)baseInputData).fixmedins_name,
  424. sign_no = ((dynamic)baseInputData).sign_no,
  425. idfi_mode = ((dynamic)baseInputData).idfi_mode,
  426. qrcode_info = ((dynamic)baseInputData).qrcode_info,
  427. // 特殊格式:使用tjfeedetail替代input
  428. tjfeedetail = businessParams ?? new object[] { }
  429. };
  430. default: // 标准交易格式
  431. return new
  432. {
  433. // 复制基础参数
  434. infno = ((dynamic)baseInputData).infno,
  435. msgid = ((dynamic)baseInputData).msgid,
  436. recer_sys_code = ((dynamic)baseInputData).recer_sys_code,
  437. infver = ((dynamic)baseInputData).infver,
  438. opter_type = ((dynamic)baseInputData).opter_type,
  439. opter = ((dynamic)baseInputData).opter,
  440. opter_name = ((dynamic)baseInputData).opter_name,
  441. inf_time = ((dynamic)baseInputData).inf_time,
  442. fixmedins_code = ((dynamic)baseInputData).fixmedins_code,
  443. fixmedins_name = ((dynamic)baseInputData).fixmedins_name,
  444. sign_no = ((dynamic)baseInputData).sign_no,
  445. idfi_mode = ((dynamic)baseInputData).idfi_mode,
  446. qrcode_info = ((dynamic)baseInputData).qrcode_info,
  447. // 标准格式:使用input
  448. input = businessParams ?? new { }
  449. };
  450. }
  451. }
  452. /// <summary>
  453. /// 解析交易输出结果并进行错误处理
  454. /// </summary>
  455. private static JObject ParseTransactionOutput(string outputJson, int dllResult, string transactionCode)
  456. {
  457. var result = new JObject();
  458. try
  459. {
  460. // DLL层面错误
  461. if (dllResult != 0)
  462. {
  463. result["success"] = false;
  464. result["code"] = 2000 + Math.Abs(dllResult);
  465. result["message"] = $"DLL调用失败,错误码: {dllResult}";
  466. result["device"] = "江苏工伤联网接口";
  467. result["dllErrorCode"] = dllResult;
  468. result["transactionCode"] = transactionCode;
  469. result["rawOutput"] = outputJson;
  470. return result;
  471. }
  472. // 解析JSON输出
  473. if (string.IsNullOrEmpty(outputJson))
  474. {
  475. result["success"] = false;
  476. result["code"] = 2001;
  477. result["message"] = "交易返回数据为空";
  478. return result;
  479. }
  480. var outputData = JObject.Parse(outputJson);
  481. // 业务层面错误检查
  482. string infcode = outputData["infcode"]?.ToString() ?? "";
  483. string errMsg = outputData["err_msg"]?.ToString() ?? "";
  484. string warnMsg = outputData["warn_msg"]?.ToString() ?? "";
  485. if (infcode == "0")
  486. {
  487. // 交易成功
  488. result["success"] = true;
  489. result["code"] = 200;
  490. result["message"] = string.IsNullOrEmpty(warnMsg) ? "交易成功" : warnMsg;
  491. result["data"] = outputData["output"];
  492. result["transactionCode"] = transactionCode;
  493. result["infRefMsgId"] = outputData["inf_refmsgid"];
  494. result["refMsgTime"] = outputData["refmsg_time"];
  495. result["respondTime"] = outputData["respond_time"];
  496. result["warnMsg"] = warnMsg;
  497. }
  498. else
  499. {
  500. // 业务失败
  501. result["success"] = false;
  502. result["code"] = 3000 + Math.Abs(int.Parse(infcode));
  503. result["message"] = string.IsNullOrEmpty(errMsg) ? "交易失败" : errMsg;
  504. result["transactionCode"] = transactionCode;
  505. result["businessErrorCode"] = infcode;
  506. result["businessErrorMsg"] = errMsg;
  507. }
  508. // 保存完整的原始返回数据
  509. result["rawOutput"] = outputJson;
  510. }
  511. catch (JsonException jsonEx)
  512. {
  513. result["success"] = false;
  514. result["code"] = 2002;
  515. result["message"] = $"返回数据解析失败: {jsonEx.Message}";
  516. result["rawOutput"] = outputJson;
  517. }
  518. catch (Exception ex)
  519. {
  520. result["success"] = false;
  521. result["code"] = 2003;
  522. result["message"] = $"输出解析异常: {ex.Message}";
  523. result["exception"] = ex.GetType().Name;
  524. }
  525. return result;
  526. }
  527. /// <summary>
  528. /// 特殊交易后处理逻辑
  529. /// </summary>
  530. private static void PostProcessTransaction(string transactionCode, JObject result)
  531. {
  532. if ((bool)result["success"])
  533. {
  534. switch (transactionCode)
  535. {
  536. case "9001": // 签到交易成功
  537. var signData = result["data"];
  538. if (signData != null)
  539. {
  540. currentSignNo = signData["sign_no"]?.ToString() ?? "";
  541. // 从服务器返回数据中获取签到时间,格式:yyyyMMddHHmmss
  542. string signTimeStr = signData["sign_time"]?.ToString() ?? "";
  543. if (!string.IsNullOrEmpty(signTimeStr) && signTimeStr.Length == 14)
  544. {
  545. try
  546. {
  547. signInTime = DateTime.ParseExact(signTimeStr, "yyyyMMddHHmmss", null);
  548. }
  549. catch
  550. {
  551. // 如果解析失败,使用本地时间作为备选
  552. signInTime = DateTime.Now;
  553. LogError($"服务器返回的签到时间格式错误: {signTimeStr},使用本地时间");
  554. }
  555. }
  556. else
  557. {
  558. // 如果服务器没有返回签到时间,使用本地时间
  559. signInTime = DateTime.Now;
  560. LogInfo($"服务器未返回签到时间,使用本地时间: {signInTime:yyyy-MM-dd HH:mm:ss}");
  561. }
  562. signInOperator = currentConfig?.DefaultOperator ?? "";
  563. // 记录签到成功日志
  564. LogInfo($"签到成功,流水号: {currentSignNo}, 时间: {signInTime:yyyy-MM-dd HH:mm:ss}");
  565. }
  566. break;
  567. case "9002": // 签退交易成功
  568. // 清除签到状态
  569. currentSignNo = "";
  570. signInTime = DateTime.MinValue;
  571. signInOperator = "";
  572. // 记录签退时间,使用统一格式
  573. string signOutTimeStr = DateTime.Now.ToString("yyyyMMddHHmmss");
  574. LogInfo($"签退成功,时间: {signOutTimeStr}");
  575. break;
  576. }
  577. }
  578. }
  579. #endregion
  580. #region 辅助工具方法
  581. /// <summary>
  582. /// 获取交易编号
  583. /// 支持中文名称、英文名称和直接的数字编号
  584. /// </summary>
  585. private static string GetTransactionCode(string transactionName)
  586. {
  587. if (string.IsNullOrEmpty(transactionName))
  588. return "";
  589. // 如果直接是4位数字编号,直接返回
  590. if (transactionName.Length == 4 && transactionName.All(char.IsDigit))
  591. return transactionName;
  592. // 从映射表中查找
  593. if (TransactionMapping.ContainsKey(transactionName))
  594. return TransactionMapping[transactionName];
  595. // 支持中文名称映射
  596. var chineseMapping = new Dictionary<string, string>
  597. {
  598. {"签到", "9001"}, {"签退", "9002"},
  599. {"读卡", "1101"}, {"登记", "2201"}, {"结算", "2207"},
  600. {"撤销", "2208"}, {"冲正", "2209"}, {"预结算", "2206"},
  601. {"处方上报", "2204"}, {"处方撤销", "2205"},
  602. {"总额对账", "1320"}, {"明细对账", "1321"},
  603. {"批量下载", "1301"}
  604. };
  605. if (chineseMapping.ContainsKey(transactionName))
  606. return chineseMapping[transactionName];
  607. return "";
  608. }
  609. /// <summary>
  610. /// 生成唯一的发送方报文ID
  611. /// 格式:协议机构编号(6位) + 时间戳(14位) + 序列号(4位) = 24位
  612. /// </summary>
  613. private static string GenerateMessageId()
  614. {
  615. lock (sequenceLock)
  616. {
  617. string institutionCode = currentConfig?.FixmedinsCode ?? "SQ201348";
  618. if (institutionCode.Length > 6)
  619. institutionCode = institutionCode.Substring(0, 6);
  620. else
  621. institutionCode = institutionCode.PadRight(6, '0');
  622. string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
  623. sequenceCounter = (sequenceCounter + 1) % 10000; // 保持4位数字
  624. string sequence = sequenceCounter.ToString("D4");
  625. return institutionCode + timestamp + sequence;
  626. }
  627. }
  628. /// <summary>
  629. /// 保存发送方报文ID用于冲正交易
  630. /// </summary>
  631. private static void SaveMessageId(string msgId, string transactionCode)
  632. {
  633. var record = new TransactionRecord
  634. {
  635. MessageId = msgId,
  636. TransactionCode = transactionCode,
  637. TransactionTime = DateTime.Now,
  638. OperatorId = currentConfig?.DefaultOperator ?? ""
  639. };
  640. transactionRecords[msgId] = record;
  641. // 清理过期记录(7天前的)
  642. var expireTime = DateTime.Now.AddDays(-7);
  643. var expiredKeys = transactionRecords
  644. .Where(kv => kv.Value.TransactionTime < expireTime)
  645. .Select(kv => kv.Key)
  646. .ToList();
  647. foreach (var key in expiredKeys)
  648. {
  649. transactionRecords.Remove(key);
  650. }
  651. }
  652. /// <summary>
  653. /// 检查DLL文件是否存在
  654. /// </summary>
  655. private static JObject CheckDllExists()
  656. {
  657. var result = new JObject();
  658. try
  659. {
  660. string programDir = AppDomain.CurrentDomain.BaseDirectory;
  661. string dllPath = Path.Combine(programDir, "JSSiInterface.dll");
  662. if (File.Exists(dllPath))
  663. {
  664. FileInfo fileInfo = new FileInfo(dllPath);
  665. result["success"] = true;
  666. result["code"] = 200;
  667. result["message"] = "JSSiInterface.dll文件检查通过";
  668. result["device"] = "江苏工伤联网接口";
  669. result["data"] = new JObject
  670. {
  671. ["dllPath"] = dllPath,
  672. ["fileSize"] = fileInfo.Length,
  673. ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss"),
  674. ["programDirectory"] = programDir
  675. };
  676. }
  677. else
  678. {
  679. result["success"] = false;
  680. result["code"] = 8001;
  681. result["message"] = "JSSiInterface.dll文件不存在";
  682. result["device"] = "江苏工伤联网接口";
  683. result["data"] = new JObject
  684. {
  685. ["expectedPath"] = dllPath,
  686. ["programDirectory"] = programDir,
  687. ["suggestion"] = "请将JSSiInterface.dll文件复制到程序目录"
  688. };
  689. }
  690. }
  691. catch (Exception ex)
  692. {
  693. result["success"] = false;
  694. result["code"] = 8002;
  695. result["message"] = $"DLL文件检查异常: {ex.Message}";
  696. result["device"] = "江苏工伤联网接口";
  697. result["exception"] = ex.GetType().Name;
  698. }
  699. return result;
  700. }
  701. /// <summary>
  702. /// 记录信息日志
  703. /// </summary>
  704. private static void LogInfo(string message)
  705. {
  706. try
  707. {
  708. if (currentConfig != null)
  709. {
  710. string logPath = Path.Combine(currentConfig.LogPath, $"workinjury_{DateTime.Now:yyyyMMdd}.log");
  711. string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [INFO] {message}{Environment.NewLine}";
  712. File.AppendAllText(logPath, logEntry);
  713. }
  714. }
  715. catch
  716. {
  717. // 日志记录失败不影响主要业务
  718. }
  719. }
  720. /// <summary>
  721. /// 记录错误日志
  722. /// </summary>
  723. private static void LogError(string message)
  724. {
  725. try
  726. {
  727. if (currentConfig != null)
  728. {
  729. string logPath = Path.Combine(currentConfig.LogPath, $"workinjury_{DateTime.Now:yyyyMMdd}.log");
  730. string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [ERROR] {message}{Environment.NewLine}";
  731. File.AppendAllText(logPath, logEntry);
  732. }
  733. }
  734. catch
  735. {
  736. // 日志记录失败不影响主要业务
  737. }
  738. }
  739. #endregion
  740. #region 便民方法集合 - 按照设计文档要求
  741. /// <summary>
  742. /// 批量上传处方明细
  743. /// 自动处理大量处方的分批上传(每批最多50条)
  744. /// </summary>
  745. public static JObject BatchUploadPrescriptions(object[] prescriptions, string patientId = "", string visitNo = "")
  746. {
  747. var result = new JObject();
  748. var results = new JArray();
  749. try
  750. {
  751. if (prescriptions == null || prescriptions.Length == 0)
  752. {
  753. result["success"] = false;
  754. result["code"] = 1006;
  755. result["message"] = "处方明细不能为空";
  756. return result;
  757. }
  758. // 分批处理(每批50条)
  759. const int batchSize = 50;
  760. int totalBatches = (int)Math.Ceiling((double)prescriptions.Length / batchSize);
  761. int successCount = 0;
  762. int failureCount = 0;
  763. for (int i = 0; i < totalBatches; i++)
  764. {
  765. var batch = prescriptions.Skip(i * batchSize).Take(batchSize).ToArray();
  766. var batchParams = new
  767. {
  768. patient_id = patientId,
  769. visit_no = visitNo,
  770. batch_no = (i + 1).ToString(),
  771. prescriptions = batch
  772. };
  773. var batchResult = ProcessWorkInjuryTransaction("UploadPrescription", batchParams);
  774. if ((bool)batchResult["success"])
  775. {
  776. successCount++;
  777. }
  778. else
  779. {
  780. failureCount++;
  781. }
  782. results.Add(new JObject
  783. {
  784. ["batchNo"] = i + 1,
  785. ["itemCount"] = batch.Length,
  786. ["success"] = batchResult["success"],
  787. ["message"] = batchResult["message"],
  788. ["code"] = batchResult["code"]
  789. });
  790. }
  791. result["success"] = failureCount == 0;
  792. result["code"] = failureCount == 0 ? 200 : 1007;
  793. result["message"] = $"批量上传完成,成功{successCount}批,失败{failureCount}批";
  794. result["data"] = new JObject
  795. {
  796. ["totalBatches"] = totalBatches,
  797. ["totalItems"] = prescriptions.Length,
  798. ["successCount"] = successCount,
  799. ["failureCount"] = failureCount,
  800. ["batchResults"] = results
  801. };
  802. }
  803. catch (Exception ex)
  804. {
  805. result["success"] = false;
  806. result["code"] = 1008;
  807. result["message"] = $"批量上传异常: {ex.Message}";
  808. result["exception"] = ex.GetType().Name;
  809. }
  810. return result;
  811. }
  812. /// <summary>
  813. /// 完整的工伤就医流程
  814. /// 自动完成:读卡 -> 登记 -> 预结算 -> 结算 的完整流程
  815. /// </summary>
  816. public static JObject CompleteWorkInjuryProcess(object patientInfo = null, object[] feeDetails = null)
  817. {
  818. var result = new JObject();
  819. var processSteps = new JArray();
  820. try
  821. {
  822. // 第1步:读卡
  823. var readCardResult = ProcessWorkInjuryTransaction("ReadCard");
  824. processSteps.Add(new JObject
  825. {
  826. ["step"] = 1,
  827. ["name"] = "读卡",
  828. ["success"] = readCardResult["success"],
  829. ["message"] = readCardResult["message"],
  830. ["data"] = readCardResult["data"]
  831. });
  832. if (!(bool)readCardResult["success"])
  833. {
  834. result["success"] = false;
  835. result["message"] = "读卡失败,流程终止";
  836. result["processSteps"] = processSteps;
  837. return result;
  838. }
  839. // 第2步:登记
  840. var registerParams = patientInfo ?? new
  841. {
  842. visit_type = "1", // 门诊
  843. dept_code = "001",
  844. dept_name = "内科",
  845. doctor_code = "DOC001",
  846. doctor_name = "张医生"
  847. };
  848. var registerResult = ProcessWorkInjuryTransaction("RegisterPatient", registerParams);
  849. processSteps.Add(new JObject
  850. {
  851. ["step"] = 2,
  852. ["name"] = "登记",
  853. ["success"] = registerResult["success"],
  854. ["message"] = registerResult["message"],
  855. ["data"] = registerResult["data"]
  856. });
  857. if (!(bool)registerResult["success"])
  858. {
  859. result["success"] = false;
  860. result["message"] = "登记失败,流程终止";
  861. result["processSteps"] = processSteps;
  862. return result;
  863. }
  864. // 如果有费用明细,进行预结算和结算
  865. if (feeDetails != null && feeDetails.Length > 0)
  866. {
  867. // 第3步:预结算
  868. var preSettleParams = new
  869. {
  870. visit_no = registerResult["data"]?["visit_no"]?.ToString() ?? "",
  871. fee_details = feeDetails
  872. };
  873. var preSettleResult = ProcessWorkInjuryTransaction("PreSettle", preSettleParams);
  874. processSteps.Add(new JObject
  875. {
  876. ["step"] = 3,
  877. ["name"] = "预结算",
  878. ["success"] = preSettleResult["success"],
  879. ["message"] = preSettleResult["message"],
  880. ["data"] = preSettleResult["data"]
  881. });
  882. if ((bool)preSettleResult["success"])
  883. {
  884. // 第4步:正式结算
  885. var settleParams = new
  886. {
  887. visit_no = registerResult["data"]?["visit_no"]?.ToString() ?? "",
  888. pre_settle_id = preSettleResult["data"]?["pre_settle_id"]?.ToString() ?? ""
  889. };
  890. var settleResult = ProcessWorkInjuryTransaction("Settle", settleParams);
  891. processSteps.Add(new JObject
  892. {
  893. ["step"] = 4,
  894. ["name"] = "结算",
  895. ["success"] = settleResult["success"],
  896. ["message"] = settleResult["message"],
  897. ["data"] = settleResult["data"]
  898. });
  899. }
  900. }
  901. result["success"] = true;
  902. result["code"] = 200;
  903. result["message"] = "工伤就医流程完成";
  904. result["processSteps"] = processSteps;
  905. }
  906. catch (Exception ex)
  907. {
  908. result["success"] = false;
  909. result["code"] = 1009;
  910. result["message"] = $"工伤就医流程异常: {ex.Message}";
  911. result["exception"] = ex.GetType().Name;
  912. result["processSteps"] = processSteps;
  913. }
  914. return result;
  915. }
  916. /// <summary>
  917. /// 智能重试交易
  918. /// 自动重试失败的交易,使用指数退避算法
  919. /// </summary>
  920. public static JObject SmartRetryTransaction(string transactionName, object businessParams = null,
  921. int maxRetries = 3, int baseDelayMs = 1000)
  922. {
  923. var result = new JObject();
  924. var retryAttempts = new JArray();
  925. for (int attempt = 1; attempt <= maxRetries; attempt++)
  926. {
  927. var attemptResult = ProcessWorkInjuryTransaction(transactionName, businessParams);
  928. retryAttempts.Add(new JObject
  929. {
  930. ["attempt"] = attempt,
  931. ["success"] = attemptResult["success"],
  932. ["message"] = attemptResult["message"],
  933. ["code"] = attemptResult["code"],
  934. ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
  935. });
  936. if ((bool)attemptResult["success"])
  937. {
  938. result = attemptResult; // 成功时返回最后一次的结果
  939. result["retryInfo"] = new JObject
  940. {
  941. ["totalAttempts"] = attempt,
  942. ["success"] = true,
  943. ["retryAttempts"] = retryAttempts
  944. };
  945. return result;
  946. }
  947. // 如果不是最后一次尝试,等待后重试
  948. if (attempt < maxRetries)
  949. {
  950. int delay = baseDelayMs * (int)Math.Pow(2, attempt - 1); // 指数退避
  951. Thread.Sleep(delay);
  952. }
  953. }
  954. // 所有重试都失败了
  955. result["success"] = false;
  956. result["code"] = 1010;
  957. result["message"] = $"交易{transactionName}经过{maxRetries}次重试后仍然失败";
  958. result["retryInfo"] = new JObject
  959. {
  960. ["totalAttempts"] = maxRetries,
  961. ["success"] = false,
  962. ["retryAttempts"] = retryAttempts
  963. };
  964. return result;
  965. }
  966. /// <summary>
  967. /// 获取交易统计信息
  968. /// 提供系统性能和健康状况监控
  969. /// </summary>
  970. public static JObject GetTransactionStatistics()
  971. {
  972. var result = new JObject();
  973. try
  974. {
  975. var stats = new JObject
  976. {
  977. ["currentTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  978. ["systemStatus"] = new JObject
  979. {
  980. ["initialized"] = isInitialized,
  981. ["signedIn"] = !string.IsNullOrEmpty(currentSignNo),
  982. ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss"),
  983. ["currentSignNo"] = currentSignNo,
  984. ["signInOperator"] = signInOperator
  985. },
  986. ["transactionRecords"] = new JObject
  987. {
  988. ["totalCount"] = transactionRecords.Count,
  989. ["oldestRecord"] = transactionRecords.Count > 0 ?
  990. transactionRecords.Values.Min(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss") : null,
  991. ["newestRecord"] = transactionRecords.Count > 0 ?
  992. transactionRecords.Values.Max(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss") : null
  993. },
  994. ["configuration"] = currentConfig != null ? new JObject
  995. {
  996. ["fixmedinsCode"] = currentConfig.FixmedinsCode,
  997. ["fixmedinsName"] = currentConfig.FixmedinsName,
  998. ["interfaceVersion"] = currentConfig.InterfaceVersion,
  999. ["logPath"] = currentConfig.LogPath
  1000. } : null
  1001. };
  1002. result["success"] = true;
  1003. result["code"] = 200;
  1004. result["message"] = "统计信息获取成功";
  1005. result["data"] = stats;
  1006. }
  1007. catch (Exception ex)
  1008. {
  1009. result["success"] = false;
  1010. result["code"] = 1011;
  1011. result["message"] = $"获取统计信息异常: {ex.Message}";
  1012. result["exception"] = ex.GetType().Name;
  1013. }
  1014. return result;
  1015. }
  1016. /// <summary>
  1017. /// 系统健康检查
  1018. /// 全面检查系统状态和连接性
  1019. /// </summary>
  1020. public static JObject HealthCheck()
  1021. {
  1022. var result = new JObject();
  1023. var checks = new JObject();
  1024. try
  1025. {
  1026. // 1. 检查DLL文件
  1027. var dllCheck = CheckDllExists();
  1028. checks["dllStatus"] = new JObject
  1029. {
  1030. ["success"] = dllCheck["success"],
  1031. ["message"] = dllCheck["message"]
  1032. };
  1033. // 2. 检查初始化状态
  1034. checks["initStatus"] = new JObject
  1035. {
  1036. ["initialized"] = isInitialized,
  1037. ["hasConfig"] = currentConfig != null
  1038. };
  1039. // 3. 检查签到状态
  1040. checks["signInStatus"] = new JObject
  1041. {
  1042. ["signedIn"] = !string.IsNullOrEmpty(currentSignNo),
  1043. ["signNo"] = currentSignNo,
  1044. ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss")
  1045. };
  1046. // 4. 尝试调用系统函数(如果已初始化)
  1047. if (isInitialized)
  1048. {
  1049. var testResult = ProcessWorkInjuryTransaction("SignIn");
  1050. checks["connectionTest"] = new JObject
  1051. {
  1052. ["success"] = testResult["success"],
  1053. ["message"] = testResult["message"]
  1054. };
  1055. }
  1056. else
  1057. {
  1058. checks["connectionTest"] = new JObject
  1059. {
  1060. ["success"] = false,
  1061. ["message"] = "系统未初始化,无法测试连接"
  1062. };
  1063. }
  1064. // 5. 检查日志目录
  1065. if (currentConfig != null)
  1066. {
  1067. checks["logStatus"] = new JObject
  1068. {
  1069. ["pathExists"] = Directory.Exists(currentConfig.LogPath),
  1070. ["logPath"] = currentConfig.LogPath,
  1071. ["writable"] = true // 简化检查,实际可以尝试写入测试
  1072. };
  1073. }
  1074. // 综合评估健康状态
  1075. bool overallHealth = (bool)checks["dllStatus"]["success"] &&
  1076. (bool)checks["initStatus"]["initialized"];
  1077. result["success"] = true;
  1078. result["code"] = 200;
  1079. result["message"] = overallHealth ? "系统健康状态良好" : "系统存在问题";
  1080. result["data"] = new JObject
  1081. {
  1082. ["overallHealth"] = overallHealth,
  1083. ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
  1084. ["checks"] = checks
  1085. };
  1086. }
  1087. catch (Exception ex)
  1088. {
  1089. result["success"] = false;
  1090. result["code"] = 1012;
  1091. result["message"] = $"健康检查异常: {ex.Message}";
  1092. result["exception"] = ex.GetType().Name;
  1093. }
  1094. return result;
  1095. }
  1096. /// <summary>
  1097. /// 获取当前签到状态
  1098. /// </summary>
  1099. public static JObject GetSignInStatus()
  1100. {
  1101. var result = new JObject();
  1102. try
  1103. {
  1104. result["success"] = true;
  1105. result["code"] = 200;
  1106. result["message"] = "签到状态获取成功";
  1107. result["data"] = new JObject
  1108. {
  1109. ["signedIn"] = !string.IsNullOrEmpty(currentSignNo),
  1110. ["signNo"] = currentSignNo,
  1111. ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss"),
  1112. ["signInOperator"] = signInOperator,
  1113. ["sessionDuration"] = signInTime == DateTime.MinValue ? null :
  1114. ((int)(DateTime.Now - signInTime).TotalMinutes).ToString() + "分钟"
  1115. };
  1116. }
  1117. catch (Exception ex)
  1118. {
  1119. result["success"] = false;
  1120. result["code"] = 1013;
  1121. result["message"] = $"获取签到状态异常: {ex.Message}";
  1122. result["exception"] = ex.GetType().Name;
  1123. }
  1124. return result;
  1125. }
  1126. /// <summary>
  1127. /// 根据交易记录进行冲正操作
  1128. /// </summary>
  1129. public static JObject ReverseTransactionByRecord(string originalMessageId)
  1130. {
  1131. var result = new JObject();
  1132. try
  1133. {
  1134. if (!transactionRecords.ContainsKey(originalMessageId))
  1135. {
  1136. result["success"] = false;
  1137. result["code"] = 1014;
  1138. result["message"] = $"找不到原交易记录: {originalMessageId}";
  1139. return result;
  1140. }
  1141. var originalRecord = transactionRecords[originalMessageId];
  1142. var reverseParams = new
  1143. {
  1144. original_msg_id = originalMessageId,
  1145. original_transaction_code = originalRecord.TransactionCode,
  1146. original_transaction_time = originalRecord.TransactionTime.ToString("yyyyMMddHHmmss"),
  1147. reverse_reason = "系统冲正"
  1148. };
  1149. result = ProcessWorkInjuryTransaction("ReverseTransaction", reverseParams);
  1150. if ((bool)result["success"])
  1151. {
  1152. // 冲正成功后,可以选择移除原记录
  1153. // transactionRecords.Remove(originalMessageId);
  1154. LogInfo($"冲正交易成功,原交易ID: {originalMessageId}");
  1155. }
  1156. }
  1157. catch (Exception ex)
  1158. {
  1159. result["success"] = false;
  1160. result["code"] = 1015;
  1161. result["message"] = $"冲正交易异常: {ex.Message}";
  1162. result["exception"] = ex.GetType().Name;
  1163. }
  1164. return result;
  1165. }
  1166. #endregion
  1167. }
  1168. }