using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace ThCardReader { /// /// 江苏工伤联网结算业务类 /// 基于JSSiInterface.dll动态库,严格按照江苏工伤联网接口规范v2.1实现 /// 参考JiangSuSocialCardBusiness.cs的成功架构模式 /// public class JiangSuWorkInjuryBusiness { #region DLL导入声明 - 严格按照工伤文档规范 /// /// 初始化函数 - 检查整个运行环境 /// /// 错误信息 /// 成功:0,失败:-1 [DllImport("JSSiInterface.dll", EntryPoint = "Si_INIT", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private extern static int Si_INIT(StringBuilder pErrMsg); /// /// 交易函数 - 处理所有业务交易 /// /// 输入参数JSON字符串 /// 输出参数JSON字符串 /// 成功:0,失败:<0 [DllImport("JSSiInterface.dll", EntryPoint = "Si_Busi", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private extern static int Si_Busi(string inputdata, StringBuilder outputdata); #endregion #region 状态管理和内部变量 /// /// 系统初始化状态 /// private static bool isInitialized = false; /// /// 当前配置信息 /// private static WorkInjuryConfig currentConfig = null; /// /// 当前签到流水号 /// private static string currentSignNo = ""; /// /// 签到时间 /// private static DateTime signInTime = DateTime.MinValue; /// /// 签到操作员 /// private static string signInOperator = ""; /// /// 报文ID缓存(用于冲正交易) /// private static readonly Dictionary transactionRecords = new Dictionary(); /// /// 序列号计数器(用于生成唯一报文ID) /// private static int sequenceCounter = 0; private static readonly object sequenceLock = new object(); /// /// 交易记录类 /// private class TransactionRecord { public string MessageId { get; set; } public string TransactionCode { get; set; } public DateTime TransactionTime { get; set; } public string OperatorId { get; set; } } #endregion #region 配置管理类 /// /// 江苏工伤联网配置类 - 严格按照工伤文档规范定义 /// public class WorkInjuryConfig { /// /// 协议机构编号 - 由江苏人社分配 /// public string FixmedinsCode { get; set; } = "SQ201348"; /// /// 协议机构名称 /// public string FixmedinsName { get; set; } = "沭阳铭和医院"; /// /// 接收方系统代码 - 默认"JSYTH" /// public string ReceiverSysCode { get; set; } = "JSYTH"; /// /// 接口版本号 - 如"V2.1" /// public string InterfaceVersion { get; set; } = "V2.1"; /// /// 经办人类别 - 1:经办人 2:自助终端 3:移动终端 /// public string OperatorType { get; set; } = "1"; /// /// 默认经办人编号 /// public string DefaultOperator { get; set; } = "001"; /// /// 默认经办人姓名 /// public string DefaultOperatorName { get; set; } = "系统管理员"; /// /// 日志路径 /// public string LogPath { get; set; } = "logs/workinjury/"; } /// /// 工伤联网交易请求参数 - 按照工伤文档标准格式 /// public class WorkInjuryTransactionRequest { /// /// 交易编号 - 4位数字 /// public string TransactionCode { get; set; } /// /// 业务参数 - 具体交易的输入参数 /// public object BusinessParams { get; set; } /// /// 识别方式 - 1:实体社保卡 2:电子凭证 /// public string IdentifyMode { get; set; } = "1"; /// /// 电子社保卡二维码(识别方式为2时必填) /// public string QrCodeInfo { get; set; } = ""; /// /// 经办人编号(可选,使用默认值) /// public string OperatorId { get; set; } = ""; /// /// 经办人姓名(可选,使用默认值) /// public string OperatorName { get; set; } = ""; } #endregion #region 交易代码映射表 /// /// 交易代码映射表 - 基于工伤文档接口列表 /// private static readonly Dictionary TransactionMapping = new Dictionary { // 认证类 {"SignIn", "9001"}, // 签到 {"SignOut", "9002"}, // 签退 // 业务类 - 核心功能 {"ReadCard", "1101"}, // 读卡 {"RegisterPatient", "2201"}, // 门诊/住院登记 {"CancelRegister", "2202"}, // 登记撤销 {"ModifyRegister", "2203"}, // 登记信息修改 {"UploadPrescription", "2204"}, // 处方明细上报 {"CancelPrescription", "2205"}, // 处方明细撤销 {"PreSettle", "2206"}, // 费用预结算 {"Settle", "2207"}, // 费用结算 {"CancelSettle", "2208"}, // 费用结算撤销 {"ReverseTransaction", "2209"}, // 冲正交易 // 体检类 - 体检协议机构使用 {"QueryExamSchedule", "8101"}, // 查询体检排班信息 {"UpdateExamSchedule", "8102"}, // 更新体检排班信息 {"QueryExamReservation", "8103"}, // 查询体检预约信息 {"ExamRegister", "8104"}, // 体检登记 {"UploadExamDetail", "8105"}, // 上传体检明细 {"QueryExamSettle", "8106"}, // 查询体检结算信息 {"QueryExamDetail", "8107"}, // 查询体检明细 {"ConfirmExamResult", "8108"}, // 体检结果确认 {"QuerySupplementCard", "8109"}, // 补刷卡登记查询 // 转院类 {"UploadReferral", "2301"}, // 转诊转院申请信息上传 {"QueryReferral", "2302"}, // 转诊转院申请信息查询 {"CancelReferral", "2303"}, // 转诊转院申请信息撤销 // 对账类 {"TotalAccount", "1320"}, // 总额对账 {"DetailAccount", "1321"}, // 明细对账 // 下载类 {"BatchDownload", "1301"}, // 批量数据下载 {"QueryFeeDetail", "9103"}, // 费用明细详细信息下载 {"QueryPrescriptionDetail", "9104"}, // 处方明细下载 {"QueryRecentVisit", "9105"} // 参保人近期就诊信息查询 }; #endregion #region 核心业务方法 - 初始化函数 /// /// 初始化江苏工伤联网系统 /// 参考JiangSuSocialCardBusiness的Initialize方法,但适配工伤业务 /// /// 配置参数 /// 初始化结果 [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] [System.Security.SecurityCritical] public static JObject Initialize(WorkInjuryConfig config = null) { var result = new JObject(); try { // 添加调试信息 result["debug_init_step"] = "开始初始化"; // 使用默认配置或传入配置 if (config == null) { config = new WorkInjuryConfig(); } result["debug_init_step"] = "配置准备完成"; // 检查DLL文件存在性 var dllCheckResult = CheckDllExists(); result["debug_dll_check"] = dllCheckResult; if (!(bool)dllCheckResult["success"]) { return dllCheckResult; } result["debug_init_step"] = "DLL检查通过"; // 确保日志目录存在 if (!Directory.Exists(config.LogPath)) { Directory.CreateDirectory(config.LogPath); } result["debug_init_step"] = "日志目录准备完成"; // 调用底层初始化函数 - 增加缓冲区大小并添加调试信息 StringBuilder errMsg = new StringBuilder(4096); // 增加缓冲区大小 result["debug_init_step"] = "准备调用Si_INIT"; // 添加DLL调用前的最后检查 try { // 测试DLL是否可以加载(不调用函数) result["debug_init_step"] = "检查DLL加载状态"; var dllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "JSSiInterface.dll"); if (!File.Exists(dllPath)) { result["success"] = false; result["code"] = 2004; result["message"] = $"DLL文件不存在: {dllPath}"; result["device"] = "江苏工伤联网接口"; result["debug_init_step"] = "DLL文件不存在"; return result; } result["debug_dll_path"] = dllPath; result["debug_dll_size"] = new FileInfo(dllPath).Length; result["debug_init_step"] = "DLL文件检查通过,准备调用Si_INIT"; } catch (Exception dllCheckEx) { result["success"] = false; result["code"] = 2005; result["message"] = $"DLL文件检查异常: {dllCheckEx.Message}"; result["device"] = "江苏工伤联网接口"; result["debug_init_step"] = "DLL文件检查异常"; result["debug_error"] = dllCheckEx.Message; return result; } int initResult; try { // 在调用前添加更多保护 result["debug_init_step"] = "即将调用Si_INIT - 这里可能发生异常"; // 设置一个标记,如果DLL调用导致进程崩溃,至少我们知道问题在这里 var beforeCallTime = DateTime.Now; result["debug_si_init_call_time"] = beforeCallTime.ToString("yyyy-MM-dd HH:mm:ss.fff"); initResult = Si_INIT(errMsg); var afterCallTime = DateTime.Now; var callDuration = (afterCallTime - beforeCallTime).TotalMilliseconds; result["debug_init_step"] = "Si_INIT调用完成"; result["debug_dll_result"] = initResult; result["debug_dll_errmsg"] = errMsg.ToString(); result["debug_si_init_duration"] = $"{callDuration}ms"; result["debug_si_init_return_time"] = afterCallTime.ToString("yyyy-MM-dd HH:mm:ss.fff"); } catch (System.AccessViolationException avEx) { result["success"] = false; result["code"] = 2001; result["message"] = "DLL访问冲突异常 - 可能是DLL版本不匹配或依赖缺失"; result["device"] = "江苏工伤联网接口"; result["exception"] = "AccessViolationException"; result["debug_init_step"] = "Si_INIT调用发生访问冲突"; result["debug_error"] = avEx.Message; return result; } catch (System.DllNotFoundException dllEx) { result["success"] = false; result["code"] = 2002; result["message"] = "找不到JSSiInterface.dll或其依赖库"; result["device"] = "江苏工伤联网接口"; result["exception"] = "DllNotFoundException"; result["debug_init_step"] = "Si_INIT调用时DLL未找到"; result["debug_error"] = dllEx.Message; return result; } catch (System.EntryPointNotFoundException epEx) { result["success"] = false; result["code"] = 2003; result["message"] = "DLL中找不到Si_INIT函数入口点"; result["device"] = "江苏工伤联网接口"; result["exception"] = "EntryPointNotFoundException"; result["debug_init_step"] = "Si_INIT函数入口点未找到"; result["debug_error"] = epEx.Message; return result; } if (initResult == 0) { isInitialized = true; currentConfig = config; result["success"] = true; result["code"] = 200; result["message"] = "江苏工伤联网系统初始化成功"; result["device"] = "江苏工伤联网接口"; result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); result["version"] = config.InterfaceVersion; result["dllErrorMsg"] = errMsg.ToString(); result["debug_init_step"] = "初始化成功"; result["config"] = new JObject { ["fixmedinsCode"] = config.FixmedinsCode, ["fixmedinsName"] = config.FixmedinsName, ["interfaceVersion"] = config.InterfaceVersion }; LogInfo($"系统初始化成功,版本: {config.InterfaceVersion}"); } else { string errorMsg = errMsg.ToString(); result["success"] = false; result["code"] = 1000 + Math.Abs(initResult); result["message"] = $"江苏工伤联网系统初始化失败: {errorMsg}"; result["device"] = "江苏工伤联网接口"; result["errorCode"] = initResult; result["dllErrorMsg"] = errorMsg; result["debug_init_step"] = "初始化失败"; LogError($"系统初始化失败,错误码: {initResult}, 错误信息: {errorMsg}"); } } catch (Exception ex) { result["success"] = false; result["code"] = 1001; result["message"] = $"江苏工伤联网系统初始化异常: {ex.Message}"; result["device"] = "江苏工伤联网接口"; result["exception"] = ex.GetType().Name; result["debug_init_step"] = "捕获到异常"; result["debug_exception_details"] = ex.ToString(); LogError($"系统初始化异常: {ex}"); } return result; } #endregion #region 核心业务方法 - 通用接口函数 /// /// 通用工伤联网交易处理接口 /// 支持所有工伤文档定义的交易类型 /// /// 交易名称(可使用中文名称或交易编号) /// 业务参数对象 /// 识别方式:1-实体社保卡,2-电子凭证 /// 电子社保卡二维码(识别方式为2时必填) /// 经办人编号(可选) /// 经办人姓名(可选) /// 交易处理结果 [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] [System.Security.SecurityCritical] public static JObject ProcessWorkInjuryTransaction( string transactionName, object businessParams = null, string identifyMode = "1", string qrCodeInfo = "", string operatorId = "", string operatorName = "") { var result = new JObject(); object inputData = null; // 新增变量,便于所有分支都能访问 try { // 1. 解析交易编号 string transactionCode = GetTransactionCode(transactionName); if (string.IsNullOrEmpty(transactionCode)) { result["success"] = false; result["code"] = 1002; result["message"] = $"不支持的交易类型: {transactionName}"; result["device"] = "江苏工伤联网接口"; return result; } // 2. 检查初始化状态 if (!isInitialized) { var autoInitResult = Initialize(); if (!(bool)autoInitResult["success"]) { result["success"] = false; result["code"] = 1003; result["message"] = "江苏工伤联网系统未初始化"; result["device"] = "江苏工伤联网接口"; result["autoInitError"] = autoInitResult["message"]; return result; } } // 3. 检查签到状态(除签到交易外) if (transactionCode != "9001" && string.IsNullOrEmpty(currentSignNo)) { var signInResult = ProcessWorkInjuryTransaction("SignIn"); if (!(bool)signInResult["success"]) { result["success"] = false; result["code"] = 1004; result["message"] = "自动签到失败,无法进行业务交易"; result["device"] = "江苏工伤联网接口"; result["signInError"] = signInResult["message"]; return result; } } // 4. 构造标准输入参数 inputData = BuildTransactionInput(transactionCode, businessParams, identifyMode, qrCodeInfo, operatorId, operatorName); string inputJson = JsonConvert.SerializeObject(inputData, Formatting.None); // 5. 调用底层DLL交易函数 - 增强异常处理 StringBuilder outputBuffer = new StringBuilder(40000); result["debug_input_json"] = inputJson; result["debug_transaction_step"] = "准备调用Si_Busi"; int dllResult; try { dllResult = Si_Busi(inputJson, outputBuffer); result["debug_transaction_step"] = "Si_Busi调用完成"; result["debug_dll_result"] = dllResult; } catch (System.AccessViolationException avEx) { result["success"] = false; result["code"] = 2011; result["message"] = $"交易{transactionName} DLL访问冲突异常"; result["device"] = "江苏工伤联网接口"; result["exception"] = "AccessViolationException"; result["debug_transaction_step"] = "Si_Busi调用发生访问冲突"; result["debug_error"] = avEx.Message; result["transactionCode"] = transactionCode; if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData); return result; } catch (System.DllNotFoundException dllEx) { result["success"] = false; result["code"] = 2012; result["message"] = $"交易{transactionName} 找不到JSSiInterface.dll"; result["device"] = "江苏工伤联网接口"; result["exception"] = "DllNotFoundException"; result["debug_transaction_step"] = "Si_Busi调用时DLL未找到"; result["debug_error"] = dllEx.Message; result["transactionCode"] = transactionCode; if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData); return result; } catch (System.EntryPointNotFoundException epEx) { result["success"] = false; result["code"] = 2013; result["message"] = $"交易{transactionName} DLL中找不到Si_Busi函数入口点"; result["device"] = "江苏工伤联网接口"; result["exception"] = "EntryPointNotFoundException"; result["debug_transaction_step"] = "Si_Busi函数入口点未找到"; result["debug_error"] = epEx.Message; result["transactionCode"] = transactionCode; if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData); return result; } // 6. 解析和处理返回结果 string outputJson = outputBuffer.ToString(); result["debug_output_json"] = outputJson; result = ParseTransactionOutput(outputJson, dllResult, transactionCode); // 7. 特殊交易后处理 PostProcessTransaction(transactionCode, result); if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData); LogInfo($"交易 {transactionName}({transactionCode}) 完成,结果: {result["success"]}"); } catch (Exception ex) { result["success"] = false; result["code"] = 1005; result["message"] = $"工伤联网交易异常: {ex.Message}"; result["device"] = "江苏工伤联网接口"; result["exception"] = ex.GetType().Name; result["transactionName"] = transactionName; if (inputData != null) result["transformed_parameters"] = JToken.FromObject(inputData); LogError($"交易 {transactionName} 异常: {ex.Message}"); } return result; } #endregion #region 输入输出处理方法 /// /// 构造标准的工伤联网交易输入参数 /// 严格按照工伤文档表2格式构造,支持特殊交易格式 /// private static object BuildTransactionInput(string transactionCode, object businessParams, string identifyMode, string qrCodeInfo, string operatorId, string operatorName) { // 生成唯一的发送方报文ID:协议机构编号(6)+时间(14)+顺序号(4) string msgId = GenerateMessageId(); // 使用配置的经办人信息或传入的参数 string finalOperatorId = string.IsNullOrEmpty(operatorId) ? currentConfig.DefaultOperator : operatorId; string finalOperatorName = string.IsNullOrEmpty(operatorName) ? currentConfig.DefaultOperatorName : operatorName; // 构造基础参数对象 var baseInputData = new { infno = transactionCode, // 交易编号 msgid = msgId, // 发送方报文ID recer_sys_code = currentConfig.ReceiverSysCode, // 接收方系统代码 infver = currentConfig.InterfaceVersion, // 接口版本号 opter_type = currentConfig.OperatorType, // 经办人类别 opter = finalOperatorId, // 经办人 opter_name = finalOperatorName, // 经办人姓名 inf_time = DateTime.Now.ToString("yyyyMMddHHmmss"), // 交易时间 fixmedins_code = currentConfig.FixmedinsCode, // 协议机构编号 fixmedins_name = currentConfig.FixmedinsName, // 协议机构名称 sign_no = transactionCode == "9001" ? "" : currentSignNo, // 签到流水号 idfi_mode = identifyMode, // 识别方式 qrcode_info = qrCodeInfo // 电子社保卡二维码 }; // 处理特殊交易格式 object finalInputData = BuildSpecialTransactionInput(baseInputData, transactionCode, businessParams); // 保存发送方报文ID用于可能的冲正操作 SaveMessageId(msgId, transactionCode); return finalInputData; } /// /// 处理特殊交易的输入格式 /// 按照工伤文档要求,某些交易需要特殊的参数格式 /// private static object BuildSpecialTransactionInput(object baseInputData, string transactionCode, object businessParams) { switch (transactionCode) { case "2204": // 处方明细上报 - 使用feedetail代替input return new { // 复制基础参数 infno = ((dynamic)baseInputData).infno, msgid = ((dynamic)baseInputData).msgid, recer_sys_code = ((dynamic)baseInputData).recer_sys_code, infver = ((dynamic)baseInputData).infver, opter_type = ((dynamic)baseInputData).opter_type, opter = ((dynamic)baseInputData).opter, opter_name = ((dynamic)baseInputData).opter_name, inf_time = ((dynamic)baseInputData).inf_time, fixmedins_code = ((dynamic)baseInputData).fixmedins_code, fixmedins_name = ((dynamic)baseInputData).fixmedins_name, sign_no = ((dynamic)baseInputData).sign_no, idfi_mode = ((dynamic)baseInputData).idfi_mode, qrcode_info = ((dynamic)baseInputData).qrcode_info, // 特殊格式:使用feedetail替代input feedetail = businessParams ?? new object[] { } }; case "8105": // 上传体检明细 - 使用tjfeedetail代替input return new { // 复制基础参数 infno = ((dynamic)baseInputData).infno, msgid = ((dynamic)baseInputData).msgid, recer_sys_code = ((dynamic)baseInputData).recer_sys_code, infver = ((dynamic)baseInputData).infver, opter_type = ((dynamic)baseInputData).opter_type, opter = ((dynamic)baseInputData).opter, opter_name = ((dynamic)baseInputData).opter_name, inf_time = ((dynamic)baseInputData).inf_time, fixmedins_code = ((dynamic)baseInputData).fixmedins_code, fixmedins_name = ((dynamic)baseInputData).fixmedins_name, sign_no = ((dynamic)baseInputData).sign_no, idfi_mode = ((dynamic)baseInputData).idfi_mode, qrcode_info = ((dynamic)baseInputData).qrcode_info, // 特殊格式:使用tjfeedetail替代input tjfeedetail = businessParams ?? new object[] { } }; default: // 标准交易格式 return new { // 复制基础参数 infno = ((dynamic)baseInputData).infno, msgid = ((dynamic)baseInputData).msgid, recer_sys_code = ((dynamic)baseInputData).recer_sys_code, infver = ((dynamic)baseInputData).infver, opter_type = ((dynamic)baseInputData).opter_type, opter = ((dynamic)baseInputData).opter, opter_name = ((dynamic)baseInputData).opter_name, inf_time = ((dynamic)baseInputData).inf_time, fixmedins_code = ((dynamic)baseInputData).fixmedins_code, fixmedins_name = ((dynamic)baseInputData).fixmedins_name, sign_no = ((dynamic)baseInputData).sign_no, idfi_mode = ((dynamic)baseInputData).idfi_mode, qrcode_info = ((dynamic)baseInputData).qrcode_info, // 标准格式:使用input input = businessParams ?? new { } }; } } /// /// 解析交易输出结果并进行错误处理 /// private static JObject ParseTransactionOutput(string outputJson, int dllResult, string transactionCode) { var result = new JObject(); try { // DLL层面错误 if (dllResult != 0) { result["success"] = false; result["code"] = 2000 + Math.Abs(dllResult); result["message"] = $"DLL调用失败,错误码: {dllResult}"; result["device"] = "江苏工伤联网接口"; result["dllErrorCode"] = dllResult; result["transactionCode"] = transactionCode; result["rawOutput"] = outputJson; return result; } // 解析JSON输出 if (string.IsNullOrEmpty(outputJson)) { result["success"] = false; result["code"] = 2001; result["message"] = "交易返回数据为空"; return result; } var outputData = JObject.Parse(outputJson); // 业务层面错误检查 string infcode = outputData["infcode"]?.ToString() ?? ""; string errMsg = outputData["err_msg"]?.ToString() ?? ""; string warnMsg = outputData["warn_msg"]?.ToString() ?? ""; if (infcode == "0") { // 交易成功 result["success"] = true; result["code"] = 200; result["message"] = string.IsNullOrEmpty(warnMsg) ? "交易成功" : warnMsg; result["data"] = outputData["output"]; result["transactionCode"] = transactionCode; result["infRefMsgId"] = outputData["inf_refmsgid"]; result["refMsgTime"] = outputData["refmsg_time"]; result["respondTime"] = outputData["respond_time"]; result["warnMsg"] = warnMsg; } else { // 业务失败 result["success"] = false; result["code"] = 3000 + Math.Abs(int.Parse(infcode)); result["message"] = string.IsNullOrEmpty(errMsg) ? "交易失败" : errMsg; result["transactionCode"] = transactionCode; result["businessErrorCode"] = infcode; result["businessErrorMsg"] = errMsg; } // 保存完整的原始返回数据 result["rawOutput"] = outputJson; } catch (JsonException jsonEx) { result["success"] = false; result["code"] = 2002; result["message"] = $"返回数据解析失败: {jsonEx.Message}"; result["rawOutput"] = outputJson; } catch (Exception ex) { result["success"] = false; result["code"] = 2003; result["message"] = $"输出解析异常: {ex.Message}"; result["exception"] = ex.GetType().Name; } return result; } /// /// 特殊交易后处理逻辑 /// private static void PostProcessTransaction(string transactionCode, JObject result) { if ((bool)result["success"]) { switch (transactionCode) { case "9001": // 签到交易成功 var signData = result["data"]; if (signData != null) { currentSignNo = signData["sign_no"]?.ToString() ?? ""; // 从服务器返回数据中获取签到时间,格式:yyyyMMddHHmmss string signTimeStr = signData["sign_time"]?.ToString() ?? ""; if (!string.IsNullOrEmpty(signTimeStr) && signTimeStr.Length == 14) { try { signInTime = DateTime.ParseExact(signTimeStr, "yyyyMMddHHmmss", null); } catch { // 如果解析失败,使用本地时间作为备选 signInTime = DateTime.Now; LogError($"服务器返回的签到时间格式错误: {signTimeStr},使用本地时间"); } } else { // 如果服务器没有返回签到时间,使用本地时间 signInTime = DateTime.Now; LogInfo($"服务器未返回签到时间,使用本地时间: {signInTime:yyyy-MM-dd HH:mm:ss}"); } signInOperator = currentConfig?.DefaultOperator ?? ""; // 记录签到成功日志 LogInfo($"签到成功,流水号: {currentSignNo}, 时间: {signInTime:yyyy-MM-dd HH:mm:ss}"); } break; case "9002": // 签退交易成功 // 清除签到状态 currentSignNo = ""; signInTime = DateTime.MinValue; signInOperator = ""; // 记录签退时间,使用统一格式 string signOutTimeStr = DateTime.Now.ToString("yyyyMMddHHmmss"); LogInfo($"签退成功,时间: {signOutTimeStr}"); break; } } } #endregion #region 辅助工具方法 /// /// 获取交易编号 /// 支持中文名称、英文名称和直接的数字编号 /// private static string GetTransactionCode(string transactionName) { if (string.IsNullOrEmpty(transactionName)) return ""; // 如果直接是4位数字编号,直接返回 if (transactionName.Length == 4 && transactionName.All(char.IsDigit)) return transactionName; // 从映射表中查找 if (TransactionMapping.ContainsKey(transactionName)) return TransactionMapping[transactionName]; // 支持中文名称映射 var chineseMapping = new Dictionary { {"签到", "9001"}, {"签退", "9002"}, {"读卡", "1101"}, {"登记", "2201"}, {"结算", "2207"}, {"撤销", "2208"}, {"冲正", "2209"}, {"预结算", "2206"}, {"处方上报", "2204"}, {"处方撤销", "2205"}, {"总额对账", "1320"}, {"明细对账", "1321"}, {"批量下载", "1301"} }; if (chineseMapping.ContainsKey(transactionName)) return chineseMapping[transactionName]; return ""; } /// /// 生成唯一的发送方报文ID /// 格式:协议机构编号(6位) + 时间戳(14位) + 序列号(4位) = 24位 /// private static string GenerateMessageId() { lock (sequenceLock) { string institutionCode = currentConfig?.FixmedinsCode ?? "SQ201348"; if (institutionCode.Length > 6) institutionCode = institutionCode.Substring(0, 6); else institutionCode = institutionCode.PadRight(6, '0'); string timestamp = DateTime.Now.ToString("yyyyMMddHHmmss"); sequenceCounter = (sequenceCounter + 1) % 10000; // 保持4位数字 string sequence = sequenceCounter.ToString("D4"); return institutionCode + timestamp + sequence; } } /// /// 保存发送方报文ID用于冲正交易 /// private static void SaveMessageId(string msgId, string transactionCode) { var record = new TransactionRecord { MessageId = msgId, TransactionCode = transactionCode, TransactionTime = DateTime.Now, OperatorId = currentConfig?.DefaultOperator ?? "" }; transactionRecords[msgId] = record; // 清理过期记录(7天前的) var expireTime = DateTime.Now.AddDays(-7); var expiredKeys = transactionRecords .Where(kv => kv.Value.TransactionTime < expireTime) .Select(kv => kv.Key) .ToList(); foreach (var key in expiredKeys) { transactionRecords.Remove(key); } } /// /// 确保DLL文件在正确位置(参考医保代码的成功实现) /// private static JObject CheckDllExists() { var result = new JObject(); try { string programDir = AppDomain.CurrentDomain.BaseDirectory; string targetDllPath = Path.Combine(programDir, "JSSiInterface.dll"); // 如果目标位置已经有DLL文件,直接返回成功 if (File.Exists(targetDllPath)) { FileInfo fileInfo = new FileInfo(targetDllPath); result["success"] = true; result["code"] = 200; result["message"] = "JSSiInterface.dll文件检查通过"; result["device"] = "江苏工伤联网接口"; result["data"] = new JObject { ["dllPath"] = targetDllPath, ["fileSize"] = fileInfo.Length, ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss"), ["programDirectory"] = programDir }; return result; } // 如果目标位置没有DLL,尝试从其他位置查找并复制(参考医保代码) string[] possiblePaths = { Path.Combine(programDir, "bin", "x86", "Release", "JSSiInterface.dll"), Path.Combine(programDir, "bin", "Debug", "JSSiInterface.dll"), Path.Combine(programDir, "bin", "Release", "JSSiInterface.dll"), Path.Combine(programDir, "ThCardReader", "bin", "x86", "Release", "JSSiInterface.dll"), Path.Combine(programDir, "ThCardReader", "bin", "Debug", "JSSiInterface.dll"), Path.Combine(programDir, "..", "ThCardReader", "bin", "x86", "Release", "JSSiInterface.dll") }; string sourcePath = null; foreach (string path in possiblePaths) { if (File.Exists(path)) { sourcePath = path; break; } } if (sourcePath != null) { // 复制DLL文件到程序目录 File.Copy(sourcePath, targetDllPath, true); FileInfo fileInfo = new FileInfo(targetDllPath); result["success"] = true; result["code"] = 200; result["message"] = "JSSiInterface.dll文件已复制到位"; result["device"] = "江苏工伤联网接口"; result["data"] = new JObject { ["sourcePath"] = sourcePath, ["targetPath"] = targetDllPath, ["fileSize"] = fileInfo.Length, ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss") }; } else { result["success"] = false; result["code"] = 8001; result["message"] = "找不到JSSiInterface.dll文件"; result["device"] = "江苏工伤联网接口"; result["data"] = new JObject { ["searchedPaths"] = string.Join("; ", possiblePaths), ["programDirectory"] = programDir, ["suggestion"] = "请确保JSSiInterface.dll文件在程序目录或bin/x86/Release目录中" }; } } catch (Exception ex) { result["success"] = false; result["code"] = 8002; result["message"] = $"DLL文件检查异常: {ex.Message}"; result["device"] = "江苏工伤联网接口"; result["exception"] = ex.GetType().Name; } return result; } /// /// 记录信息日志 /// private static void LogInfo(string message) { try { if (currentConfig != null) { string logPath = Path.Combine(currentConfig.LogPath, $"workinjury_{DateTime.Now:yyyyMMdd}.log"); string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [INFO] {message}{Environment.NewLine}"; File.AppendAllText(logPath, logEntry); } } catch { // 日志记录失败不影响主要业务 } } /// /// 记录错误日志 /// private static void LogError(string message) { try { if (currentConfig != null) { string logPath = Path.Combine(currentConfig.LogPath, $"workinjury_{DateTime.Now:yyyyMMdd}.log"); string logEntry = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] [ERROR] {message}{Environment.NewLine}"; File.AppendAllText(logPath, logEntry); } } catch { // 日志记录失败不影响主要业务 } } #endregion #region 便民方法集合 - 按照设计文档要求 /// /// 批量上传处方明细 /// 自动处理大量处方的分批上传(每批最多50条) /// public static JObject BatchUploadPrescriptions(object[] prescriptions, string patientId = "", string visitNo = "") { var result = new JObject(); var results = new JArray(); try { if (prescriptions == null || prescriptions.Length == 0) { result["success"] = false; result["code"] = 1006; result["message"] = "处方明细不能为空"; return result; } // 分批处理(每批50条) const int batchSize = 50; int totalBatches = (int)Math.Ceiling((double)prescriptions.Length / batchSize); int successCount = 0; int failureCount = 0; for (int i = 0; i < totalBatches; i++) { var batch = prescriptions.Skip(i * batchSize).Take(batchSize).ToArray(); var batchParams = new { patient_id = patientId, visit_no = visitNo, batch_no = (i + 1).ToString(), prescriptions = batch }; var batchResult = ProcessWorkInjuryTransaction("UploadPrescription", batchParams); if ((bool)batchResult["success"]) { successCount++; } else { failureCount++; } results.Add(new JObject { ["batchNo"] = i + 1, ["itemCount"] = batch.Length, ["success"] = batchResult["success"], ["message"] = batchResult["message"], ["code"] = batchResult["code"] }); } result["success"] = failureCount == 0; result["code"] = failureCount == 0 ? 200 : 1007; result["message"] = $"批量上传完成,成功{successCount}批,失败{failureCount}批"; result["data"] = new JObject { ["totalBatches"] = totalBatches, ["totalItems"] = prescriptions.Length, ["successCount"] = successCount, ["failureCount"] = failureCount, ["batchResults"] = results }; } catch (Exception ex) { result["success"] = false; result["code"] = 1008; result["message"] = $"批量上传异常: {ex.Message}"; result["exception"] = ex.GetType().Name; } return result; } /// /// 完整的工伤就医流程 /// 自动完成:读卡 -> 登记 -> 预结算 -> 结算 的完整流程 /// public static JObject CompleteWorkInjuryProcess(object patientInfo = null, object[] feeDetails = null) { var result = new JObject(); var processSteps = new JArray(); try { // 第1步:读卡 var readCardResult = ProcessWorkInjuryTransaction("ReadCard"); processSteps.Add(new JObject { ["step"] = 1, ["name"] = "读卡", ["success"] = readCardResult["success"], ["message"] = readCardResult["message"], ["data"] = readCardResult["data"] }); if (!(bool)readCardResult["success"]) { result["success"] = false; result["message"] = "读卡失败,流程终止"; result["processSteps"] = processSteps; return result; } // 第2步:登记 var registerParams = patientInfo ?? new { visit_type = "1", // 门诊 dept_code = "001", dept_name = "内科", doctor_code = "DOC001", doctor_name = "张医生" }; var registerResult = ProcessWorkInjuryTransaction("RegisterPatient", registerParams); processSteps.Add(new JObject { ["step"] = 2, ["name"] = "登记", ["success"] = registerResult["success"], ["message"] = registerResult["message"], ["data"] = registerResult["data"] }); if (!(bool)registerResult["success"]) { result["success"] = false; result["message"] = "登记失败,流程终止"; result["processSteps"] = processSteps; return result; } // 如果有费用明细,进行预结算和结算 if (feeDetails != null && feeDetails.Length > 0) { // 第3步:预结算 var preSettleParams = new { visit_no = registerResult["data"]?["visit_no"]?.ToString() ?? "", fee_details = feeDetails }; var preSettleResult = ProcessWorkInjuryTransaction("PreSettle", preSettleParams); processSteps.Add(new JObject { ["step"] = 3, ["name"] = "预结算", ["success"] = preSettleResult["success"], ["message"] = preSettleResult["message"], ["data"] = preSettleResult["data"] }); if ((bool)preSettleResult["success"]) { // 第4步:正式结算 var settleParams = new { visit_no = registerResult["data"]?["visit_no"]?.ToString() ?? "", pre_settle_id = preSettleResult["data"]?["pre_settle_id"]?.ToString() ?? "" }; var settleResult = ProcessWorkInjuryTransaction("Settle", settleParams); processSteps.Add(new JObject { ["step"] = 4, ["name"] = "结算", ["success"] = settleResult["success"], ["message"] = settleResult["message"], ["data"] = settleResult["data"] }); } } result["success"] = true; result["code"] = 200; result["message"] = "工伤就医流程完成"; result["processSteps"] = processSteps; } catch (Exception ex) { result["success"] = false; result["code"] = 1009; result["message"] = $"工伤就医流程异常: {ex.Message}"; result["exception"] = ex.GetType().Name; result["processSteps"] = processSteps; } return result; } /// /// 智能重试交易 /// 自动重试失败的交易,使用指数退避算法 /// public static JObject SmartRetryTransaction(string transactionName, object businessParams = null, int maxRetries = 3, int baseDelayMs = 1000) { var result = new JObject(); var retryAttempts = new JArray(); for (int attempt = 1; attempt <= maxRetries; attempt++) { var attemptResult = ProcessWorkInjuryTransaction(transactionName, businessParams); retryAttempts.Add(new JObject { ["attempt"] = attempt, ["success"] = attemptResult["success"], ["message"] = attemptResult["message"], ["code"] = attemptResult["code"], ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") }); if ((bool)attemptResult["success"]) { result = attemptResult; // 成功时返回最后一次的结果 result["retryInfo"] = new JObject { ["totalAttempts"] = attempt, ["success"] = true, ["retryAttempts"] = retryAttempts }; return result; } // 如果不是最后一次尝试,等待后重试 if (attempt < maxRetries) { int delay = baseDelayMs * (int)Math.Pow(2, attempt - 1); // 指数退避 Thread.Sleep(delay); } } // 所有重试都失败了 result["success"] = false; result["code"] = 1010; result["message"] = $"交易{transactionName}经过{maxRetries}次重试后仍然失败"; result["retryInfo"] = new JObject { ["totalAttempts"] = maxRetries, ["success"] = false, ["retryAttempts"] = retryAttempts }; return result; } /// /// 获取交易统计信息 /// 提供系统性能和健康状况监控 /// public static JObject GetTransactionStatistics() { var result = new JObject(); try { var stats = new JObject { ["currentTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), ["systemStatus"] = new JObject { ["initialized"] = isInitialized, ["signedIn"] = !string.IsNullOrEmpty(currentSignNo), ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss"), ["currentSignNo"] = currentSignNo, ["signInOperator"] = signInOperator }, ["transactionRecords"] = new JObject { ["totalCount"] = transactionRecords.Count, ["oldestRecord"] = transactionRecords.Count > 0 ? transactionRecords.Values.Min(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss") : null, ["newestRecord"] = transactionRecords.Count > 0 ? transactionRecords.Values.Max(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss") : null }, ["configuration"] = currentConfig != null ? new JObject { ["fixmedinsCode"] = currentConfig.FixmedinsCode, ["fixmedinsName"] = currentConfig.FixmedinsName, ["interfaceVersion"] = currentConfig.InterfaceVersion, ["logPath"] = currentConfig.LogPath } : null }; result["success"] = true; result["code"] = 200; result["message"] = "统计信息获取成功"; result["data"] = stats; } catch (Exception ex) { result["success"] = false; result["code"] = 1011; result["message"] = $"获取统计信息异常: {ex.Message}"; result["exception"] = ex.GetType().Name; } return result; } /// /// 系统健康检查 /// 全面检查系统状态和连接性 /// public static JObject HealthCheck() { var result = new JObject(); var checks = new JObject(); try { // 1. 检查DLL文件 var dllCheck = CheckDllExists(); checks["dllStatus"] = new JObject { ["success"] = dllCheck["success"], ["message"] = dllCheck["message"] }; // 2. 检查初始化状态 checks["initStatus"] = new JObject { ["initialized"] = isInitialized, ["hasConfig"] = currentConfig != null }; // 3. 检查签到状态 checks["signInStatus"] = new JObject { ["signedIn"] = !string.IsNullOrEmpty(currentSignNo), ["signNo"] = currentSignNo, ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss") }; // 4. 尝试调用系统函数(如果已初始化) if (isInitialized) { var testResult = ProcessWorkInjuryTransaction("SignIn"); checks["connectionTest"] = new JObject { ["success"] = testResult["success"], ["message"] = testResult["message"] }; } else { checks["connectionTest"] = new JObject { ["success"] = false, ["message"] = "系统未初始化,无法测试连接" }; } // 5. 检查日志目录 if (currentConfig != null) { checks["logStatus"] = new JObject { ["pathExists"] = Directory.Exists(currentConfig.LogPath), ["logPath"] = currentConfig.LogPath, ["writable"] = true // 简化检查,实际可以尝试写入测试 }; } // 综合评估健康状态 bool overallHealth = (bool)checks["dllStatus"]["success"] && (bool)checks["initStatus"]["initialized"]; result["success"] = true; result["code"] = 200; result["message"] = overallHealth ? "系统健康状态良好" : "系统存在问题"; result["data"] = new JObject { ["overallHealth"] = overallHealth, ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), ["checks"] = checks }; } catch (Exception ex) { result["success"] = false; result["code"] = 1012; result["message"] = $"健康检查异常: {ex.Message}"; result["exception"] = ex.GetType().Name; } return result; } /// /// 获取当前签到状态 /// public static JObject GetSignInStatus() { var result = new JObject(); try { result["success"] = true; result["code"] = 200; result["message"] = "签到状态获取成功"; result["data"] = new JObject { ["signedIn"] = !string.IsNullOrEmpty(currentSignNo), ["signNo"] = currentSignNo, ["signInTime"] = signInTime == DateTime.MinValue ? null : signInTime.ToString("yyyy-MM-dd HH:mm:ss"), ["signInOperator"] = signInOperator, ["sessionDuration"] = signInTime == DateTime.MinValue ? null : ((int)(DateTime.Now - signInTime).TotalMinutes).ToString() + "分钟" }; } catch (Exception ex) { result["success"] = false; result["code"] = 1013; result["message"] = $"获取签到状态异常: {ex.Message}"; result["exception"] = ex.GetType().Name; } return result; } /// /// 根据交易记录进行冲正操作 /// public static JObject ReverseTransactionByRecord(string originalMessageId) { var result = new JObject(); try { if (!transactionRecords.ContainsKey(originalMessageId)) { result["success"] = false; result["code"] = 1014; result["message"] = $"找不到原交易记录: {originalMessageId}"; return result; } var originalRecord = transactionRecords[originalMessageId]; var reverseParams = new { original_msg_id = originalMessageId, original_transaction_code = originalRecord.TransactionCode, original_transaction_time = originalRecord.TransactionTime.ToString("yyyyMMddHHmmss"), reverse_reason = "系统冲正" }; result = ProcessWorkInjuryTransaction("ReverseTransaction", reverseParams); if ((bool)result["success"]) { // 冲正成功后,可以选择移除原记录 // transactionRecords.Remove(originalMessageId); LogInfo($"冲正交易成功,原交易ID: {originalMessageId}"); } } catch (Exception ex) { result["success"] = false; result["code"] = 1015; result["message"] = $"冲正交易异常: {ex.Message}"; result["exception"] = ex.GetType().Name; } return result; } #endregion } }