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