using System; using System.Runtime.InteropServices; using System.Text; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace JiangSuElectronicCertificate { /// /// 江苏医保电子凭证解码接口 /// 基于江苏医保接口规范v0.9.9.15和HeaSecReadInfo.dll实现 /// 符合国家医保电子凭证业务标准动态库交互规范2021-11-12 /// public class JiangSuEcDecoder { #region DLL导入声明 /// /// 1.14.1 初始化 /// /// JSON格式的初始化参数 /// 错误信息输出缓冲区 /// 0-成功,其他-失败 [DllImport("HeaSecReadInfo.dll", EntryPoint = "Init", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private extern static Int32 Init(string pInitInfo, StringBuilder pErrMsg); /// /// 1.14.6 电子凭证解码 /// /// 输入数据(JSON格式) /// 输出数据缓冲区 /// 0-成功,其他-失败 [DllImport("HeaSecReadInfo.dll", EntryPoint = "EcQuery", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)] private extern static Int32 EcQuery(string pInData, StringBuilder pOutData); #endregion #region 配置类 /// /// 江苏医保配置信息 /// public class JiangSuConfig { /// /// 服务器IP地址(根据具体医院配置修改) /// public string IP { get; set; } = "10.61.165.3"; /// /// 服务器端口号 /// public int PORT { get; set; } = 8086; /// /// 超时时间(秒) /// public int TIMEOUT { get; set; } = 30; /// /// 日志路径(根据项目实际路径修改) /// public string LOG_PATH { get; set; } = "C:\\logs\\"; /// /// 电子凭证解码URL(根据具体医院配置修改) /// public string EC_URL { get; set; } = "http://10.58.33.207:10086/localcfc/api/hsecfc/localQrCodeQuery"; /// /// 卡片通道类型 /// public string CARD_PASSTYPE { get; set; } = "2"; /// /// API名称 /// public string API_NAME { get; set; } = "hssServives"; /// /// API版本 /// public string API_VERSION { get; set; } = "1.0.0"; /// /// 访问密钥(根据具体医院申请的密钥修改) /// public string ACCESS_KEY { get; set; } = "090ea4c914324ee38b6978365df46a80"; /// /// 秘钥(根据具体医院申请的密钥修改) /// public string SECRETKEY { get; set; } = "CMgTEMfftMP0EMMoliOs65wZmv8="; /// /// 机构编号(根据具体医院的医保定点编号修改) /// public string ORG_ID { get; set; } = "H32132200561"; /// /// 扩展参数 /// public string EXT { get; set; } = "{}"; /// /// 地区编码 /// public string AREA_CODE { get; set; } = "321322"; /// /// 转换为JSON格式 /// /// JSON字符串 public string ToJson() { var config = new { IP = this.IP, PORT = this.PORT, TIMEOUT = this.TIMEOUT, LOG_PATH = this.LOG_PATH, EC_URL = this.EC_URL, CARD_PASSTYPE = this.CARD_PASSTYPE, API_NAME = this.API_NAME, API_VERSION = this.API_VERSION, ACCESS_KEY = this.ACCESS_KEY, SECRETKEY = this.SECRETKEY, ORG_ID = this.ORG_ID, EXT = this.EXT, AREA_CODE = this.AREA_CODE }; return JsonConvert.SerializeObject(config, Formatting.None); } } #endregion #region 私有变量 private static bool isInitialized = false; private static JiangSuConfig currentConfig = null; #endregion #region 公共方法 /// /// 初始化江苏医保系统 /// /// 配置信息,为null时使用默认配置 /// 初始化结果 public static JObject Initialize(JiangSuConfig config = null) { var result = new JObject(); try { // 使用传入的配置或默认配置 if (config == null) { config = new JiangSuConfig(); } currentConfig = config; // 准备初始化参数 string initParams = config.ToJson(); // 准备错误信息缓冲区 StringBuilder errorBuffer = new StringBuilder(1024); // 调用DLL初始化函数 int initResult = Init(initParams, errorBuffer); if (initResult == 0) { isInitialized = true; result["success"] = true; result["code"] = 200; result["message"] = "江苏医保系统初始化成功"; result["device"] = "江苏医保电子凭证解码器"; result["config"] = JObject.Parse(initParams); result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); } else { isInitialized = false; string errorMessage = errorBuffer.ToString(); result["success"] = false; result["code"] = 1000 + initResult; result["message"] = $"江苏医保系统初始化失败,错误码: {initResult}"; result["device"] = "江苏医保电子凭证解码器"; result["dllErrorCode"] = initResult; result["dllErrorMessage"] = errorMessage; } } catch (Exception ex) { isInitialized = false; result["success"] = false; result["code"] = 1001; result["message"] = $"江苏医保系统初始化异常: {ex.Message}"; result["device"] = "江苏医保电子凭证解码器"; result["exception"] = ex.GetType().Name; } return result; } /// /// 江苏医保电子凭证解码 /// /// 业务类型编码(默认01101门诊挂号) /// 收款员编号 /// 收款员姓名 /// 医保科室编号 /// 科室名称 /// 解码结果 public static JObject DecodeElectronicCertificate( string businessType = "01101", string operatorId = "system001", string operatorName = "系统管理员", string officeId = "32760", string officeName = "医保科") { var result = new JObject(); try { // 检查初始化状态 if (!isInitialized) { // 自动初始化 var autoInitResult = Initialize(); if (!(bool)autoInitResult["success"]) { result["success"] = false; result["code"] = 1002; result["message"] = "江苏医保系统未初始化,电子凭证解码失败"; result["device"] = "江苏医保电子凭证解码器"; result["autoInitError"] = autoInitResult["message"]; result["type"] = "qrcode"; return result; } } // 获取当前配置 if (currentConfig == null) { result["success"] = false; result["code"] = 1003; result["message"] = "江苏医保配置信息为空"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; return result; } // 按照江苏医保接口规范1.14.6构造输入参数 var inputData = new { data = new { orgId = currentConfig.ORG_ID, // 定点编号 businessType = businessType, // 用码业务类型 operatorId = operatorId, // 收款员编号 operatorName = operatorName, // 收款员姓名 officeId = officeId, // 医保科室编号 officeName = officeName // 科室名称 }, transType = "ec.query", // 固定值:ec.query orgId = currentConfig.ORG_ID // 定点编号 }; // JSON序列化输入参数 string jsonInput = JsonConvert.SerializeObject(inputData, Formatting.None); // 按照规范分配8192字节输出缓冲区 StringBuilder outputBuffer = new StringBuilder(8192); // 调用江苏医保DLL的EcQuery函数 int dllResult = EcQuery(jsonInput, outputBuffer); if (dllResult == 0) { // 解析DLL返回的JSON数据 string responseJson = outputBuffer.ToString(); if (string.IsNullOrEmpty(responseJson)) { result["success"] = false; result["code"] = 1004; result["message"] = "电子凭证解码返回数据为空"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; return result; } // 解析江苏医保返回的JSON数据 var jiangsuResponse = JObject.Parse(responseJson); // 检查江苏医保接口返回的code字段 int responseCode = (int)(jiangsuResponse["code"] ?? -1); if (responseCode == 0) { // 成功:提取患者信息 var patientData = jiangsuResponse["data"]; if (patientData != null) { result["success"] = true; result["code"] = 200; result["message"] = "江苏医保电子凭证解码成功"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; result["originalType"] = "jiangsu_ec"; result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); // 按照文档规范提取患者信息 var patientInfo = new { idNo = patientData["idNo"]?.ToString() ?? "", // 身份证号 userName = patientData["userName"]?.ToString() ?? "", // 姓名 idType = patientData["idType"]?.ToString() ?? "", // 证件类型 ecToken = patientData["ecToken"]?.ToString() ?? "", // 令牌 insuOrg = patientData["insuOrg"]?.ToString() ?? "", // 参保地区编码 ecIndexNo = patientData["ecIndexNo"]?.ToString() ?? "", // 电子凭证索引号 gender = patientData["gender"]?.ToString() ?? "", // 性别 birthday = patientData["birthday"]?.ToString() ?? "", // 出生日期 nationality = patientData["nationality"]?.ToString() ?? "", // 国籍 email = patientData["email"]?.ToString() ?? "", // 邮箱 extra = patientData["extra"]?.ToString() ?? "" // 扩展参数 }; // 按照泰和医院格式构造data字段(包含code/data/message结构) var dllResponseFormat = new { code = 0, data = patientInfo, message = "交易成功" }; result["data"] = JsonConvert.SerializeObject(dllResponseFormat); // 保存原始江苏医保返回数据(用于调试和日志) result["jiangsuOriginalData"] = responseJson; result["businessType"] = businessType; result["interfaceVersion"] = "v0.9.9.15"; } else { result["success"] = false; result["code"] = 1005; result["message"] = "电子凭证解码成功,但患者数据为空"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; result["jiangsuOriginalData"] = responseJson; } } else { // 江苏医保接口返回失败 string errorMessage = jiangsuResponse["message"]?.ToString() ?? "电子凭证解码失败"; result["success"] = false; result["code"] = 2000 + responseCode; // 使用2xxx系列表示江苏医保接口错误 result["message"] = $"江苏医保电子凭证解码失败: {errorMessage}"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; result["jiangsuErrorCode"] = responseCode; result["jiangsuErrorMessage"] = errorMessage; result["jiangsuOriginalData"] = responseJson; result["businessType"] = businessType; } } else { // DLL函数调用失败 string errorInfo = outputBuffer.ToString(); result["success"] = false; result["code"] = 3000 + dllResult; // 使用3xxx系列表示DLL调用错误 result["message"] = $"江苏医保DLL调用失败,错误码: {dllResult}"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; result["dllErrorCode"] = dllResult; result["dllErrorMessage"] = errorInfo; result["businessType"] = businessType; result["suggestion"] = GetEcQueryErrorSuggestion(dllResult); } } catch (JsonException jsonEx) { result["success"] = false; result["code"] = 1006; result["message"] = $"电子凭证解码数据解析异常: {jsonEx.Message}"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; result["exception"] = jsonEx.GetType().Name; result["businessType"] = businessType; } catch (Exception ex) { result["success"] = false; result["code"] = 1007; result["message"] = $"电子凭证解码系统异常: {ex.Message}"; result["device"] = "江苏医保电子凭证解码器"; result["type"] = "qrcode"; result["exception"] = ex.GetType().Name; result["businessType"] = businessType; } return result; } /// /// 检查业务类型编码是否有效 /// /// 业务类型编码 /// 是否有效 public static bool IsValidBusinessType(string businessType) { if (string.IsNullOrEmpty(businessType) || businessType.Length != 5) return false; // 有效的业务类型列表(根据国家医保电子凭证业务标准) string[] validTypes = { "01101", // 医院-挂号 "01102", // 医院-住院建档 "01103", // 医院-入院登记 "01104", // 医院-缴纳预缴金 "01201", // 医院-问诊 "01202", // 医院-预约检查 "01203", // 医院-检查 "01204", // 医院-治疗 "01301", // 医院-结算 "01302", // 医院-取药 "01303", // 医院-取报告 "01304", // 医院-打印票据和清单 "01305", // 医院-病历材料复印 "01306", // 医院-诊间核验身份 "02121", // 药店-药店购药 "02122", // 药店-下载外购处方 "02123", // 药店-特殊门诊 "02124", // 药店-药师审核处方 "03131", // 医疗类APP-线上身份认证 "03132", // 医疗类APP-线上结算 "05101", // 柜台-线下修改密码 "05151" // 柜台-医保业务办理 }; return Array.IndexOf(validTypes, businessType) >= 0; } #endregion #region 私有方法 /// /// 获取电子凭证查询错误建议 /// /// 错误码 /// 错误建议 private static string GetEcQueryErrorSuggestion(int errorCode) { switch (errorCode) { case -1: return "请检查网络连接和服务器配置"; case -2: return "请检查输入参数是否正确"; case -3: return "请求超时,请稍后重试"; case -4: return "权限验证失败,请检查密钥配置"; case -5: return "请求地址无效,请检查EC_URL配置"; default: return "请联系技术支持或查看详细错误信息"; } } #endregion } #region Web API控制器示例 /// /// 江苏医保电子凭证Web API控制器示例 /// 可以直接集成到ASP.NET Web API项目中 /// public class JiangSuEcController : System.Web.Http.ApiController { /// /// 电子凭证解码接口 /// POST /api/jiangsuec/decode /// /// 请求参数 /// 解码结果 [System.Web.Http.HttpPost] [System.Web.Http.Route("api/jiangsuec/decode")] public JObject Decode([System.Web.Http.FromBody] dynamic request) { try { string businessType = request?.businessType ?? "01101"; string operatorId = request?.operatorId ?? "system001"; string operatorName = request?.operatorName ?? "系统管理员"; string officeId = request?.officeId ?? "32760"; string officeName = request?.officeName ?? "医保科"; return JiangSuEcDecoder.DecodeElectronicCertificate( businessType, operatorId, operatorName, officeId, officeName); } catch (Exception ex) { var errorResult = new JObject(); errorResult["success"] = false; errorResult["code"] = 9001; errorResult["message"] = $"接口调用异常: {ex.Message}"; errorResult["device"] = "江苏医保电子凭证解码器"; errorResult["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); return errorResult; } } /// /// 初始化接口 /// POST /api/jiangsuec/init /// /// 配置参数 /// 初始化结果 [System.Web.Http.HttpPost] [System.Web.Http.Route("api/jiangsuec/init")] public JObject Init([System.Web.Http.FromBody] dynamic request) { try { JiangSuEcDecoder.JiangSuConfig config = null; if (request?.config != null) { config = new JiangSuEcDecoder.JiangSuConfig(); if (request.config.IP != null) config.IP = (string)request.config.IP; if (request.config.PORT != null) config.PORT = (int)request.config.PORT; if (request.config.ORG_ID != null) config.ORG_ID = (string)request.config.ORG_ID; if (request.config.ACCESS_KEY != null) config.ACCESS_KEY = (string)request.config.ACCESS_KEY; if (request.config.SECRETKEY != null) config.SECRETKEY = (string)request.config.SECRETKEY; if (request.config.EC_URL != null) config.EC_URL = (string)request.config.EC_URL; } return JiangSuEcDecoder.Initialize(config); } catch (Exception ex) { var errorResult = new JObject(); errorResult["success"] = false; errorResult["code"] = 9002; errorResult["message"] = $"初始化接口异常: {ex.Message}"; errorResult["device"] = "江苏医保电子凭证解码器"; errorResult["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); return errorResult; } } } #endregion } #region 使用示例 /* // 使用示例1:基本调用 public void Example1() { // 1. 初始化(可选,会自动初始化) var initResult = JiangSuEcDecoder.Initialize(); Console.WriteLine($"初始化结果: {initResult}"); // 2. 解码电子凭证(门诊挂号) var decodeResult = JiangSuEcDecoder.DecodeElectronicCertificate( businessType: "01101", // 门诊挂号 operatorId: "OP001", // 操作员编号 operatorName: "张医生", // 操作员姓名 officeId: "32760", // 科室编号 officeName: "内科" // 科室名称 ); Console.WriteLine($"解码结果: {decodeResult}"); if ((bool)decodeResult["success"]) { var patientData = decodeResult["data"]; Console.WriteLine($"患者姓名: {patientData["userName"]}"); Console.WriteLine($"身份证号: {patientData["idNo"]}"); Console.WriteLine($"电子凭证令牌: {patientData["ecToken"]}"); } } // 使用示例2:自定义配置 public void Example2() { // 创建自定义配置 var config = new JiangSuEcDecoder.JiangSuConfig { IP = "192.168.1.100", // 医院内网IP PORT = 8086, // 端口 ORG_ID = "H32132200561", // 医保定点编号 ACCESS_KEY = "your_access_key_here", // 申请的访问密钥 SECRETKEY = "your_secret_key_here", // 申请的秘钥 EC_URL = "http://your.hospital.com:10086/localcfc/api/hsecfc/localQrCodeQuery" }; // 使用自定义配置初始化 var initResult = JiangSuEcDecoder.Initialize(config); // 解码电子凭证 var decodeResult = JiangSuEcDecoder.DecodeElectronicCertificate("01301"); // 门诊结算 } // 使用示例3:Web API集成 public class YourController : ApiController { [HttpPost] public JObject DecodeQRCode([FromBody] dynamic request) { string businessType = request?.type ?? "01101"; return JiangSuEcDecoder.DecodeElectronicCertificate(businessType); } } // 使用示例4:JavaScript前端调用 /* // 前端JavaScript调用示例 $.ajax({ url: "http://localhost:8321/api/jiangsuec/decode", type: "POST", contentType: "application/json", data: JSON.stringify({ businessType: "01101", // 门诊挂号 operatorId: "OP001", // 操作员编号 operatorName: "张医生", // 操作员姓名 officeId: "32760", // 科室编号 officeName: "内科" // 科室名称 }), success: function(result) { if (result.success) { console.log("解码成功:", result.data); alert("患者: " + result.data.userName + ", 身份证: " + result.data.idNo); } else { console.log("解码失败:", result.message); alert("解码失败: " + result.message); } }, error: function(xhr, status, error) { console.log("请求失败:", error); alert("网络请求失败"); } }); */ /* */ #endregion