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
}
}