版本号 | 修改内容 | 修改日期 | 修改人 |
---|---|---|---|
V1.0 | 初始版本设计 | 2024.12.19 | 系统 |
基于江苏省工伤机构联网结算接口开发手册(V2.1版本),设计一个统一的工伤联网接口对接业务类,实现与江苏省工伤联网系统的无缝对接。
JiangSuSocialCardBusiness.cs
的成功模式,提供统一的业务接口采用用户建议的实现方案:
┌─────────────────────────────────────────┐
│ HIS医院信息系统 │
├─────────────────────────────────────────┤
│ JiangSuWorkInjuryBusiness │
│ ┌─────────────┬─────────────────────┐ │
│ │ 初始化函数 │ 通用接口函数 │ │
│ │ Initialize │ ProcessTransaction │ │
│ └─────────────┴─────────────────────┘ │
├─────────────────────────────────────────┤
│ JSSiInterface.dll │
│ ┌─────────────┬─────────────────────┐ │
│ │ Si_INIT │ Si_Busi │ │
│ └─────────────┴─────────────────────┘ │
├─────────────────────────────────────────┤
│ HTTPS/SSL安全传输协议 │
├─────────────────────────────────────────┤
│ 江苏省工伤联网中心系统 │
└─────────────────────────────────────────┘
根据工伤文档,接口共分为4大类:
分类 | 交易编号范围 | 主要功能 | 实现策略 |
---|---|---|---|
对账类 | 1320-1321 | 总额对账、明细对账 | 统一对账处理 |
认证类 | 9001-9002 | 签到、签退 | 会话管理 |
业务类 | 1101, 22XX, 81XX, 23XX | 读卡、登记、结算、体检、转院 | 核心业务处理 |
下载类 | 1301, 91XX | 数据下载、查询 | 数据同步 |
/// <summary>
/// 江苏工伤联网结算业务类
/// 基于JSSiInterface.dll动态库,严格按照江苏工伤联网接口规范v2.1实现
/// 参考JiangSuSocialCardBusiness.cs的成功架构模式
/// </summary>
public class JiangSuWorkInjuryBusiness
{
#region DLL导入声明 - 严格按照工伤文档规范
/// <summary>
/// 初始化函数 - 检查整个运行环境
/// </summary>
/// <param name="pErrMsg">错误信息</param>
/// <returns>成功:0,失败:-1</returns>
[DllImport("JSSiInterface.dll", EntryPoint = "Si_INIT", CharSet = CharSet.Ansi)]
private extern static int Si_INIT(StringBuilder pErrMsg);
/// <summary>
/// 交易函数 - 处理所有业务交易
/// </summary>
/// <param name="inputdata">输入参数JSON字符串</param>
/// <param name="outputdata">输出参数JSON字符串</param>
/// <returns>成功:0,失败:<0</returns>
[DllImport("JSSiInterface.dll", EntryPoint = "Si_Busi", CharSet = CharSet.Ansi)]
private extern static int Si_Busi(string inputdata, StringBuilder outputdata);
#endregion
#region 状态管理和内部变量
/// <summary>
/// 系统初始化状态
/// </summary>
private static bool isInitialized = false;
/// <summary>
/// 当前配置信息
/// </summary>
private static WorkInjuryConfig currentConfig = null;
/// <summary>
/// 当前签到流水号
/// </summary>
private static string currentSignNo = "";
/// <summary>
/// 签到时间
/// </summary>
private static DateTime signInTime = DateTime.MinValue;
/// <summary>
/// 签到操作员
/// </summary>
private static string signInOperator = "";
/// <summary>
/// 报文ID缓存(用于冲正交易)
/// </summary>
private static readonly Dictionary<string, TransactionRecord> transactionRecords = new Dictionary<string, TransactionRecord>();
/// <summary>
/// 序列号计数器(用于生成唯一报文ID)
/// </summary>
private static int sequenceCounter = 0;
private static readonly object sequenceLock = new object();
/// <summary>
/// 交易记录类
/// </summary>
private class TransactionRecord
{
public string MessageId { get; set; }
public string TransactionCode { get; set; }
public DateTime TransactionTime { get; set; }
public string OperatorId { get; set; }
}
#endregion
}
/// <summary>
/// 江苏工伤联网配置类 - 严格按照工伤文档规范定义
/// </summary>
public class WorkInjuryConfig
{
/// <summary>
/// 协议机构编号 - 由江苏人社分配
/// </summary>
public string FixmedinsCode { get; set; } = "H00001";
/// <summary>
/// 协议机构名称
/// </summary>
public string FixmedinsName { get; set; } = "第一人民医院";
/// <summary>
/// 接收方系统代码 - 默认"JSYTH"
/// </summary>
public string ReceiverSysCode { get; set; } = "JSYTH";
/// <summary>
/// 接口版本号 - 如"V2.1"
/// </summary>
public string InterfaceVersion { get; set; } = "V2.1";
/// <summary>
/// 经办人类别 - 1:经办人 2:自助终端 3:移动终端
/// </summary>
public string OperatorType { get; set; } = "1";
/// <summary>
/// 默认经办人编号
/// </summary>
public string DefaultOperator { get; set; } = "001";
/// <summary>
/// 默认经办人姓名
/// </summary>
public string DefaultOperatorName { get; set; } = "系统管理员";
}
/// <summary>
/// 工伤联网交易请求参数 - 按照工伤文档标准格式
/// </summary>
public class WorkInjuryTransactionRequest
{
/// <summary>
/// 交易编号 - 4位数字
/// </summary>
public string TransactionCode { get; set; }
/// <summary>
/// 业务参数 - 具体交易的输入参数
/// </summary>
public object BusinessParams { get; set; }
/// <summary>
/// 识别方式 - 1:实体社保卡 2:电子凭证
/// </summary>
public string IdentifyMode { get; set; } = "1";
/// <summary>
/// 电子社保卡二维码(识别方式为2时必填)
/// </summary>
public string QrCodeInfo { get; set; } = "";
/// <summary>
/// 经办人编号(可选,使用默认值)
/// </summary>
public string OperatorId { get; set; } = "";
/// <summary>
/// 经办人姓名(可选,使用默认值)
/// </summary>
public string OperatorName { get; set; } = "";
}
public static JObject ProcessTransaction(WorkInjuryTransactionRequest request)
{
var result = new JObject();
try
{
// 1. 检查初始化状态
if (!isInitialized)
{
var initResult = Initialize();
if (!(bool)initResult["success"])
{
return initResult;
}
}
// 2. 构造标准输入参数
var inputJson = BuildStandardInput(request);
// 3. 调用底层DLL
StringBuilder outputBuffer = new StringBuilder(40000);
int dllResult = Si_Busi(inputJson, outputBuffer);
// 4. 解析输出结果
result = ParseStandardOutput(outputBuffer.ToString(), dllResult);
// 5. 特殊业务处理
result = ProcessSpecialBusiness(request.TransactionCode, result);
}
catch (Exception ex)
{
result = BuildErrorResult(ex);
}
return result;
}
/// <summary>
/// 交易代码映射表 - 基于工伤文档接口列表
/// </summary>
private static readonly Dictionary<string, string> TransactionMapping = new Dictionary<string, string>
{
// 认证类
{"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"} // 参保人近期就诊信息查询
};
/// <summary>
/// 初始化江苏工伤联网系统
/// 参考JiangSuSocialCardBusiness的Initialize方法,但适配工伤业务
/// </summary>
/// <param name="config">配置参数</param>
/// <returns>初始化结果</returns>
public static JObject Initialize(WorkInjuryConfig config = null)
{
var result = new JObject();
try
{
// 使用默认配置或传入配置
if (config == null)
{
config = new WorkInjuryConfig();
}
// 检查DLL文件存在性
var dllCheckResult = CheckDllExists();
if (!(bool)dllCheckResult["success"])
{
return dllCheckResult;
}
// 调用底层初始化函数
StringBuilder errMsg = new StringBuilder(1024);
int initResult = Si_INIT(errMsg);
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();
}
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;
}
}
catch (Exception ex)
{
result["success"] = false;
result["code"] = 1001;
result["message"] = $"江苏工伤联网系统初始化异常: {ex.Message}";
result["device"] = "江苏工伤联网接口";
result["exception"] = ex.GetType().Name;
}
return result;
}
/// <summary>
/// 通用工伤联网交易处理接口
/// 支持所有工伤文档定义的交易类型
/// </summary>
/// <param name="transactionName">交易名称(可使用中文名称或交易编号)</param>
/// <param name="businessParams">业务参数对象</param>
/// <param name="identifyMode">识别方式:1-实体社保卡,2-电子凭证</param>
/// <param name="qrCodeInfo">电子社保卡二维码(识别方式为2时必填)</param>
/// <param name="operatorId">经办人编号(可选)</param>
/// <param name="operatorName">经办人姓名(可选)</param>
/// <returns>交易处理结果</returns>
public static JObject ProcessWorkInjuryTransaction(
string transactionName,
object businessParams = null,
string identifyMode = "1",
string qrCodeInfo = "",
string operatorId = "",
string operatorName = "")
{
var result = new JObject();
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. 构造标准输入参数
var inputData = BuildTransactionInput(transactionCode, businessParams, identifyMode, qrCodeInfo, operatorId, operatorName);
string inputJson = JsonConvert.SerializeObject(inputData, Formatting.None);
// 5. 调用底层DLL交易函数
StringBuilder outputBuffer = new StringBuilder(40000);
int dllResult = Si_Busi(inputJson, outputBuffer);
// 6. 解析和处理返回结果
string outputJson = outputBuffer.ToString();
result = ParseTransactionOutput(outputJson, dllResult, transactionCode);
// 7. 特殊交易后处理
PostProcessTransaction(transactionCode, result);
}
catch (Exception ex)
{
result["success"] = false;
result["code"] = 1005;
result["message"] = $"工伤联网交易异常: {ex.Message}";
result["device"] = "江苏工伤联网接口";
result["exception"] = ex.GetType().Name;
result["transactionName"] = transactionName;
}
return result;
}
/// <summary>
/// 特殊交易后处理逻辑
/// </summary>
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() ?? "";
signInTime = DateTime.Now;
signInOperator = currentConfig?.DefaultOperator ?? "";
// 记录签到成功日志
LogInfo($"签到成功,流水号: {currentSignNo}, 时间: {signInTime:yyyy-MM-dd HH:mm:ss}");
}
break;
case "9002": // 签退交易成功
// 清除签到状态
currentSignNo = "";
signInTime = DateTime.MinValue;
signInOperator = "";
LogInfo($"签退成功,时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}");
break;
}
}
}
/// <summary>
/// 构造标准的工伤联网交易输入参数
/// 严格按照工伤文档表2格式构造,支持特殊交易格式
/// </summary>
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;
}
/// <summary>
/// 处理特殊交易的输入格式
/// 按照工伤文档要求,某些交易需要特殊的参数格式
/// </summary>
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 { }
};
}
}
// 为常用交易提供便捷方法,简化调用
/// <summary>
/// 读卡交易 - 获取工伤人员基本信息
/// </summary>
public static JObject ReadWorkInjuryCard(string identifyMode = "1", string qrCodeInfo = "")
{
return ProcessWorkInjuryTransaction("ReadCard", null, identifyMode, qrCodeInfo);
}
/// <summary>
/// 门诊/住院登记
/// </summary>
public static JObject RegisterPatient(object registerParams, string identifyMode = "1")
{
return ProcessWorkInjuryTransaction("RegisterPatient", registerParams, identifyMode);
}
/// <summary>
/// 费用预结算
/// </summary>
public static JObject PreSettlement(object settlementParams)
{
return ProcessWorkInjuryTransaction("PreSettle", settlementParams);
}
/// <summary>
/// 费用结算
/// </summary>
public static JObject Settlement(object settlementParams)
{
return ProcessWorkInjuryTransaction("Settle", settlementParams);
}
/// <summary>
/// 处方明细上传
/// </summary>
public static JObject UploadPrescription(object prescriptionDetails)
{
return ProcessWorkInjuryTransaction("UploadPrescription", prescriptionDetails);
}
/// <summary>
/// 签到交易
/// </summary>
public static JObject SignIn()
{
return ProcessWorkInjuryTransaction("SignIn");
}
/// <summary>
/// 签退交易
/// </summary>
public static JObject SignOut()
{
return ProcessWorkInjuryTransaction("SignOut");
}
/// <summary>
/// 冲正交易
/// </summary>
public static JObject ReverseTransaction(string originalTransactionCode, string originalMessageId)
{
var reverseParams = new
{
infno = originalTransactionCode,
msgid = originalMessageId
};
return ProcessWorkInjuryTransaction("ReverseTransaction", reverseParams);
}
/// <summary>
/// 根据交易记录进行冲正(便捷方法)
/// </summary>
public static JObject ReverseTransactionByRecord(string messageId)
{
if (transactionRecords.ContainsKey(messageId))
{
var record = transactionRecords[messageId];
return ReverseTransaction(record.TransactionCode, record.MessageId);
}
else
{
return new JObject
{
["success"] = false,
["code"] = 1006,
["message"] = $"未找到报文ID对应的交易记录: {messageId}",
["device"] = "江苏工伤联网接口"
};
}
}
/// <summary>
/// 获取当前签到状态
/// </summary>
public static JObject GetSignInStatus()
{
return new JObject
{
["success"] = true,
["code"] = 200,
["message"] = "签到状态查询成功",
["data"] = new JObject
{
["isSignedIn"] = !string.IsNullOrEmpty(currentSignNo),
["signNo"] = currentSignNo,
["signInTime"] = signInTime == DateTime.MinValue ? "" : signInTime.ToString("yyyy-MM-dd HH:mm:ss"),
["signInOperator"] = signInOperator,
["isInitialized"] = isInitialized
}
};
}
/// <summary>
/// 批量处方明细上传 - 便捷方法
/// </summary>
public static JObject BatchUploadPrescriptions(List<object> prescriptionList, int batchSize = 50)
{
var result = new JObject();
var successResults = new JArray();
var failedResults = new JArray();
try
{
if (prescriptionList == null || prescriptionList.Count == 0)
{
result["success"] = false;
result["code"] = 1007;
result["message"] = "处方明细列表为空";
return result;
}
// 按批次处理(单次最多50条)
for (int i = 0; i < prescriptionList.Count; i += batchSize)
{
var batch = prescriptionList.Skip(i).Take(batchSize).ToList();
var batchResult = UploadPrescription(batch);
if ((bool)batchResult["success"])
{
successResults.Add(new JObject
{
["batchIndex"] = i / batchSize + 1,
["count"] = batch.Count,
["result"] = batchResult
});
}
else
{
failedResults.Add(new JObject
{
["batchIndex"] = i / batchSize + 1,
["count"] = batch.Count,
["result"] = batchResult
});
}
}
result["success"] = failedResults.Count == 0;
result["code"] = result["success"] ? 200 : 1008;
result["message"] = $"批量上传完成,成功{successResults.Count}批,失败{failedResults.Count}批";
result["data"] = new JObject
{
["totalBatches"] = (prescriptionList.Count + batchSize - 1) / batchSize,
["successBatches"] = successResults.Count,
["failedBatches"] = failedResults.Count,
["successResults"] = successResults,
["failedResults"] = failedResults
};
}
catch (Exception ex)
{
result["success"] = false;
result["code"] = 1009;
result["message"] = $"批量上传异常: {ex.Message}";
result["exception"] = ex.GetType().Name;
}
return result;
}
/// <summary>
/// 完整的工伤患者就诊流程 - 一站式便捷方法
/// </summary>
public static JObject CompleteWorkInjuryProcess(object patientRegisterParams, List<object> prescriptionList, object settlementParams)
{
var result = new JObject();
var processSteps = new JArray();
try
{
// 步骤1:读卡
var readCardResult = ReadWorkInjuryCard();
processSteps.Add(new JObject { ["step"] = "读卡", ["result"] = readCardResult });
if (!(bool)readCardResult["success"])
{
result["success"] = false;
result["message"] = "读卡失败,流程终止";
result["processSteps"] = processSteps;
return result;
}
// 步骤2:患者登记
var registerResult = RegisterPatient(patientRegisterParams);
processSteps.Add(new JObject { ["step"] = "患者登记", ["result"] = registerResult });
if (!(bool)registerResult["success"])
{
result["success"] = false;
result["message"] = "患者登记失败,流程终止";
result["processSteps"] = processSteps;
return result;
}
// 步骤3:上传处方明细
var uploadResult = BatchUploadPrescriptions(prescriptionList);
processSteps.Add(new JObject { ["step"] = "上传处方", ["result"] = uploadResult });
if (!(bool)uploadResult["success"])
{
result["success"] = false;
result["message"] = "处方上传失败,流程终止";
result["processSteps"] = processSteps;
return result;
}
// 步骤4:费用预结算
var preSettleResult = PreSettlement(settlementParams);
processSteps.Add(new JObject { ["step"] = "费用预结算", ["result"] = preSettleResult });
if (!(bool)preSettleResult["success"])
{
result["success"] = false;
result["message"] = "费用预结算失败,流程终止";
result["processSteps"] = processSteps;
return result;
}
// 步骤5:正式结算
var settleResult = Settlement(settlementParams);
processSteps.Add(new JObject { ["step"] = "正式结算", ["result"] = settleResult });
result["success"] = (bool)settleResult["success"];
result["code"] = result["success"] ? 200 : 1010;
result["message"] = result["success"] ? "工伤就诊流程全部完成" : "正式结算失败";
result["processSteps"] = processSteps;
result["finalSettlementData"] = settleResult["data"];
}
catch (Exception ex)
{
result["success"] = false;
result["code"] = 1011;
result["message"] = $"工伤就诊流程异常: {ex.Message}";
result["processSteps"] = processSteps;
result["exception"] = ex.GetType().Name;
}
return result;
}
/// <summary>
/// 智能重试交易 - 带指数退避算法
/// </summary>
public static JObject SmartRetryTransaction(string transactionName, object businessParams,
int maxRetries = 3, int baseDelayMs = 1000, double backoffMultiplier = 2.0)
{
JObject lastResult = null;
for (int attempt = 0; attempt < maxRetries; attempt++)
{
try
{
lastResult = ProcessWorkInjuryTransaction(transactionName, businessParams);
// 成功则立即返回
if ((bool)lastResult["success"])
{
lastResult["retryInfo"] = new JObject
{
["attemptCount"] = attempt + 1,
["maxRetries"] = maxRetries,
["finalAttempt"] = true
};
return lastResult;
}
// 判断是否值得重试
int errorCode = (int)lastResult["code"];
if (!IsRetryableError(errorCode))
{
lastResult["retryInfo"] = new JObject
{
["attemptCount"] = attempt + 1,
["maxRetries"] = maxRetries,
["retryable"] = false,
["reason"] = "错误类型不可重试"
};
break;
}
// 如果不是最后一次尝试,则等待后重试
if (attempt < maxRetries - 1)
{
int delayMs = (int)(baseDelayMs * Math.Pow(backoffMultiplier, attempt));
Thread.Sleep(delayMs);
LogInfo($"交易 {transactionName} 第 {attempt + 1} 次尝试失败,{delayMs}ms后重试");
}
}
catch (Exception ex)
{
lastResult = new JObject
{
["success"] = false,
["code"] = 1012,
["message"] = $"智能重试异常: {ex.Message}",
["exception"] = ex.GetType().Name,
["retryInfo"] = new JObject
{
["attemptCount"] = attempt + 1,
["maxRetries"] = maxRetries,
["exceptionAttempt"] = true
}
};
}
}
// 所有重试都失败了
if (lastResult != null)
{
lastResult["retryInfo"] = new JObject
{
["attemptCount"] = maxRetries,
["maxRetries"] = maxRetries,
["allAttemptsFailed"] = true
};
}
return lastResult ?? new JObject
{
["success"] = false,
["message"] = "智能重试完全失败"
};
}
/// <summary>
/// 交易统计和监控 - 获取交易统计信息
/// </summary>
public static JObject GetTransactionStatistics()
{
try
{
var stats = new JObject
{
["totalTransactions"] = transactionRecords.Count,
["oldestTransaction"] = transactionRecords.Values.Any()
? transactionRecords.Values.Min(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss")
: "",
["newestTransaction"] = transactionRecords.Values.Any()
? transactionRecords.Values.Max(r => r.TransactionTime).ToString("yyyy-MM-dd HH:mm:ss")
: "",
["transactionsByType"] = new JObject()
};
// 按交易类型统计
var groupedTransactions = transactionRecords.Values
.GroupBy(r => r.TransactionCode)
.ToDictionary(g => g.Key, g => g.Count());
foreach (var kvp in groupedTransactions)
{
((JObject)stats["transactionsByType"])[kvp.Key] = kvp.Value;
}
return new JObject
{
["success"] = true,
["code"] = 200,
["message"] = "交易统计获取成功",
["data"] = stats,
["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
};
}
catch (Exception ex)
{
return new JObject
{
["success"] = false,
["code"] = 1013,
["message"] = $"获取交易统计异常: {ex.Message}",
["exception"] = ex.GetType().Name
};
}
}
/// <summary>
/// 系统健康检查 - 全面的系统状态检查
/// </summary>
public static JObject HealthCheck()
{
var healthResult = new JObject();
var checks = new JObject();
bool overallHealthy = true;
try
{
// 1. DLL文件检查
var dllCheck = CheckDllExists();
checks["dllFile"] = dllCheck;
if (!(bool)dllCheck["success"]) overallHealthy = false;
// 2. 初始化状态检查
checks["initialization"] = new JObject
{
["status"] = isInitialized ? "initialized" : "not_initialized",
["healthy"] = isInitialized
};
if (!isInitialized) overallHealthy = false;
// 3. 签到状态检查
var signInStatus = GetSignInStatus();
checks["signInStatus"] = signInStatus;
bool isSignedIn = (bool)signInStatus["data"]["isSignedIn"];
if (!isSignedIn) overallHealthy = false;
// 4. 配置检查
checks["configuration"] = new JObject
{
["hasConfig"] = currentConfig != null,
["fixmedinsCode"] = currentConfig?.FixmedinsCode ?? "",
["interfaceVersion"] = currentConfig?.InterfaceVersion ?? ""
};
if (currentConfig == null) overallHealthy = false;
// 5. 内存使用检查
var process = System.Diagnostics.Process.GetCurrentProcess();
checks["memory"] = new JObject
{
["workingSetMB"] = Math.Round(process.WorkingSet64 / 1024.0 / 1024.0, 2),
["privateMemoryMB"] = Math.Round(process.PrivateMemorySize64 / 1024.0 / 1024.0, 2)
};
// 6. 交易记录检查
checks["transactionRecords"] = new JObject
{
["count"] = transactionRecords.Count,
["healthy"] = transactionRecords.Count < 10000 // 避免内存泄漏
};
healthResult["success"] = true;
healthResult["code"] = 200;
healthResult["message"] = overallHealthy ? "系统健康状态良好" : "系统存在健康问题";
healthResult["healthy"] = overallHealthy;
healthResult["checks"] = checks;
healthResult["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
}
catch (Exception ex)
{
healthResult["success"] = false;
healthResult["code"] = 1014;
healthResult["message"] = $"健康检查异常: {ex.Message}";
healthResult["healthy"] = false;
healthResult["exception"] = ex.GetType().Name;
}
return healthResult;
}
#endregion
#region 辅助方法
/// <summary>
/// 生成唯一的发送方报文ID
/// 格式:协议机构编号(6位)+时间(14位)+顺序号(4位)
/// </summary>
private static string GenerateMessageId()
{
lock (sequenceLock)
{
sequenceCounter++;
if (sequenceCounter > 9999)
{
sequenceCounter = 1;
}
string fixmedinsCode = currentConfig?.FixmedinsCode?.PadLeft(6, '0') ?? "000000";
string timeStr = DateTime.Now.ToString("yyyyMMddHHmmss");
string sequenceStr = sequenceCounter.ToString("D4");
return $"{fixmedinsCode}{timeStr}{sequenceStr}";
}
}
/// <summary>
/// 保存交易记录(用于冲正)
/// </summary>
private static void SaveMessageId(string messageId, string transactionCode)
{
try
{
var record = new TransactionRecord
{
MessageId = messageId,
TransactionCode = transactionCode,
TransactionTime = DateTime.Now,
OperatorId = currentConfig?.DefaultOperator ?? ""
};
transactionRecords[messageId] = record;
// 清理30天前的记录(避免内存泄漏)
var expireTime = DateTime.Now.AddDays(-30);
var expiredKeys = transactionRecords.Where(r => r.Value.TransactionTime < expireTime)
.Select(r => r.Key)
.ToList();
foreach (var key in expiredKeys)
{
transactionRecords.Remove(key);
}
}
catch (Exception ex)
{
LogError($"保存交易记录失败: {ex.Message}");
}
}
/// <summary>
/// 获取交易编号(支持中文名称和数字编号)
/// </summary>
private static string GetTransactionCode(string transactionName)
{
if (string.IsNullOrEmpty(transactionName))
return "";
// 如果已经是4位数字编号,直接返回
if (transactionName.Length == 4 && transactionName.All(char.IsDigit))
{
return transactionName;
}
// 从映射表中查找
return TransactionMapping.ContainsKey(transactionName)
? TransactionMapping[transactionName]
: "";
}
/// <summary>
/// 检查DLL文件是否存在
/// </summary>
private static JObject CheckDllExists()
{
var result = new JObject();
try
{
string programDir = AppDomain.CurrentDomain.BaseDirectory;
string dllPath = Path.Combine(programDir, "JSSiInterface.dll");
if (File.Exists(dllPath))
{
FileInfo fileInfo = new FileInfo(dllPath);
result["success"] = true;
result["code"] = 200;
result["message"] = "JSSiInterface.dll文件检查通过";
result["data"] = new JObject
{
["dllPath"] = dllPath,
["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["data"] = new JObject
{
["expectedPath"] = dllPath,
["suggestion"] = "请将JSSiInterface.dll文件复制到程序目录"
};
}
}
catch (Exception ex)
{
result["success"] = false;
result["code"] = 8002;
result["message"] = $"DLL文件检查异常: {ex.Message}";
result["exception"] = ex.GetType().Name;
}
return result;
}
/// <summary>
/// 判断是否为可重试的错误
/// </summary>
private static bool IsRetryableError(int errorCode)
{
// 网络相关错误可重试
int[] retryableErrors = { 1004, 2000, 2001, 3001, 3002 };
return retryableErrors.Contains(errorCode);
}
/// <summary>
/// 日志记录 - 信息级别
/// </summary>
private static void LogInfo(string message)
{
try
{
string logDir = "logs/workinjury/";
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
string logFile = Path.Combine(logDir, $"workinjury_{DateTime.Now:yyyyMMdd}.log");
string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [INFO] {message}{Environment.NewLine}";
File.AppendAllText(logFile, logEntry, Encoding.UTF8);
}
catch
{
// 日志记录失败不影响主业务
}
}
/// <summary>
/// 日志记录 - 错误级别
/// </summary>
private static void LogError(string message)
{
try
{
string logDir = "logs/workinjury/";
if (!Directory.Exists(logDir))
{
Directory.CreateDirectory(logDir);
}
string logFile = Path.Combine(logDir, $"workinjury_{DateTime.Now:yyyyMMdd}.log");
string logEntry = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} [ERROR] {message}{Environment.NewLine}";
File.AppendAllText(logFile, logEntry, Encoding.UTF8);
}
catch
{
// 日志记录失败不影响主业务
}
}
#endregion
}
/// <summary>
/// 解析交易输出结果并进行错误处理
/// </summary>
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;
}
/// <summary>
/// 带重试机制的交易处理
/// </summary>
public static JObject ProcessWithRetry(string transactionName, object businessParams, int maxRetries = 3)
{
for (int i = 0; i < maxRetries; i++)
{
var result = ProcessWorkInjuryTransaction(transactionName, businessParams);
// 成功则直接返回
if ((bool)result["success"])
{
return result;
}
// 判断是否可重试的错误
int errorCode = (int)result["code"];
if (IsRetryableError(errorCode) && i < maxRetries - 1)
{
// 等待重试
Thread.Sleep(1000 * (i + 1)); // 递增等待时间
continue;
}
// 不可重试或已达最大重试次数
result["retryCount"] = i + 1;
return result;
}
// 理论上不会到达这里
return new JObject { ["success"] = false, ["message"] = "重试机制异常" };
}
// HIS系统中的调用示例
public class HisWorkInjuryService
{
/// <summary>
/// 工伤患者挂号登记业务流程
/// </summary>
public async Task<HisResult> WorkInjuryRegisterAsync(HisPatientInfo patientInfo)
{
try
{
// 1. 读卡获取工伤人员信息
var readCardResult = JiangSuWorkInjuryBusiness.ReadWorkInjuryCard();
if (!(bool)readCardResult["success"])
{
return HisResult.Fail("读卡失败: " + readCardResult["message"]);
}
// 2. 构造登记参数
var registerParams = new
{
ipt_otp_no = GenerateUniqueNo(), // 门诊流水号
med_type = patientInfo.MedicalType, // 医疗类别
adm_time = DateTime.Now.ToString("yyyyMMddHHmmss"), // 入院时间
adm_diag_dscr = patientInfo.DiagnosisCode, // 诊断疾病编码
adm_dept_codg = patientInfo.DepartmentCode, // 科室编码
atddr_no = patientInfo.DoctorCode, // 医生编码
psn_no = readCardResult["data"]["psn_no"], // 个人唯一识别码
qualification_id = ExtractQualificationId(readCardResult) // 工伤医疗费资格审核信息ID
};
// 3. 执行登记交易
var registerResult = JiangSuWorkInjuryBusiness.RegisterPatient(registerParams);
if (!(bool)registerResult["success"])
{
return HisResult.Fail("登记失败: " + registerResult["message"]);
}
// 4. 保存HIS本地数据
await SaveLocalPatientData(patientInfo, readCardResult, registerResult);
return HisResult.Success("工伤患者登记成功");
}
catch (Exception ex)
{
return HisResult.Fail($"工伤登记异常: {ex.Message}");
}
}
/// <summary>
/// 工伤费用结算业务流程
/// </summary>
public async Task<HisResult> WorkInjurySettleAsync(string visitNo)
{
try
{
// 1. 验证患者身份
var readCardResult = JiangSuWorkInjuryBusiness.ReadWorkInjuryCard();
if (!(bool)readCardResult["success"])
{
return HisResult.Fail("患者身份验证失败");
}
// 2. 上传处方明细
var prescriptionData = await GetPrescriptionData(visitNo);
var uploadResult = JiangSuWorkInjuryBusiness.UploadPrescription(prescriptionData);
if (!(bool)uploadResult["success"])
{
return HisResult.Fail("处方上传失败: " + uploadResult["message"]);
}
// 3. 费用预结算
var preSettleParams = BuildSettlementParams(visitNo, isPreSettle: true);
var preSettleResult = JiangSuWorkInjuryBusiness.PreSettlement(preSettleParams);
if (!(bool)preSettleResult["success"])
{
return HisResult.Fail("预结算失败: " + preSettleResult["message"]);
}
// 4. 正式结算
var settleParams = BuildSettlementParams(visitNo, isPreSettle: false);
var settleResult = JiangSuWorkInjuryBusiness.Settlement(settleParams);
if (!(bool)settleResult["success"])
{
return HisResult.Fail("结算失败: " + settleResult["message"]);
}
// 5. 保存结算结果
await SaveSettlementResult(visitNo, settleResult);
return HisResult.Success("工伤费用结算成功", settleResult["data"]);
}
catch (Exception ex)
{
return HisResult.Fail($"工伤结算异常: {ex.Message}");
}
}
}
<!-- app.config 配置示例 -->
<configuration>
<appSettings>
<!-- 江苏工伤联网配置 -->
<add key="WorkInjury.FixmedinsCode" value="H00001" />
<add key="WorkInjury.FixmedinsName" value="第一人民医院" />
<add key="WorkInjury.ReceiverSysCode" value="JSYTH" />
<add key="WorkInjury.InterfaceVersion" value="V2.1" />
<add key="WorkInjury.OperatorType" value="1" />
<add key="WorkInjury.DefaultOperator" value="001" />
<add key="WorkInjury.DefaultOperatorName" value="系统管理员" />
<!-- 自动重试配置 -->
<add key="WorkInjury.MaxRetries" value="3" />
<add key="WorkInjury.RetryInterval" value="1000" />
<!-- 日志配置 -->
<add key="WorkInjury.LogLevel" value="Info" />
<add key="WorkInjury.LogPath" value="logs/workinjury/" />
</appSettings>
</configuration>
复制DLL文件
将 JSSiInterface.dll 复制到应用程序bin目录
确保DLL文件版本与接口文档版本一致
配置参数设置
根据医院实际情况修改配置文件中的参数
协议机构编号和名称需要与江苏人社备案信息一致
权限配置
确保应用程序有读取DLL文件的权限
配置网络访问权限
测试验证
先在测试环境验证接口连通性
执行签到/签退测试确认基础功能
进行完整业务流程测试
JiangSuSocialCardBusiness.cs
架构模式这个设计方案完全符合您的思路,提供了一个初始化函数和一个通用接口函数,通过参数来区分不同的交易类型,可以直接嵌套到HIS系统中使用,大大简化了工伤联网接口的集成复杂度。