123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995 |
- using System;
- using System.IO;
- using System.Linq;
- using System.Runtime.InteropServices;
- using System.Text;
- using Newtonsoft.Json;
- using Newtonsoft.Json.Linq;
- namespace ThCardReader
- {
- /// <summary>
- /// 江苏医保社保卡读取业务类
- /// 基于HeaSecReadInfo.dll作为主DLL,严格按照江苏医保规范实现
- /// </summary>
- public class JiangSuSocialCardBusiness
- {
- #region DLL导入声明
- // 主DLL: HeaSecReadInfo.dll - 严格按照江苏医保规范v0.9.9.15实现
-
- /// <summary>
- /// 1.14.1 初始化
- /// </summary>
- /// <param name="pInitInfo">JSON格式的初始化参数</param>
- /// <param name="pErrMsg">错误信息输出缓冲区</param>
- /// <returns>0-成功,其他-失败</returns>
- [DllImport("HeaSecReadInfo.dll", EntryPoint = "Init", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static Int32 Init(string pInitInfo, StringBuilder pErrMsg);
- /// <summary>
- /// 1.14.2 读社保卡基本信息
- /// </summary>
- /// <param name="pCardInfo">社保卡基本信息输出缓冲区</param>
- /// <param name="pBusiCardInfo">社保卡业务信息输出缓冲区</param>
- /// <returns>0-成功,其他-失败</returns>
- [DllImport("HeaSecReadInfo.dll", EntryPoint = "ReadCardBas", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static Int32 ReadCardBas(StringBuilder pCardInfo, StringBuilder pBusiCardInfo);
- /// <summary>
- /// 1.14.3 检验PIN码
- /// </summary>
- /// <param name="pOutBuff">输出信息缓冲区</param>
- /// <returns>0-成功,其他-失败</returns>
- [DllImport("HeaSecReadInfo.dll", EntryPoint = "VerifyPIN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static Int32 VerifyPIN(StringBuilder pOutBuff);
- /// <summary>
- /// 1.14.4 修改PIN码
- /// </summary>
- /// <param name="pOutBuff">输出信息缓冲区</param>
- /// <returns>0-成功,其他-失败</returns>
- [DllImport("HeaSecReadInfo.dll", EntryPoint = "ChangePIN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static Int32 ChangePIN(StringBuilder pOutBuff);
- /// <summary>
- /// 1.14.6 电子凭证解码
- /// </summary>
- /// <param name="pInData">输入数据(JSON格式)</param>
- /// <param name="pOutData">输出数据缓冲区</param>
- /// <returns>0-成功,其他-失败</returns>
- [DllImport("HeaSecReadInfo.dll", EntryPoint = "EcQuery", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static Int32 EcQuery(string pInData, StringBuilder pOutData);
- /// <summary>
- /// 1.14.8 四合一介质获得个人信息
- /// </summary>
- /// <param name="pInData">输入数据</param>
- /// <param name="pOutData">输出数据缓冲区</param>
- /// <returns>0-成功,其他-失败</returns>
- [DllImport("HeaSecReadInfo.dll", EntryPoint = "GetPersonInfo", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static Int32 GetPersonInfo(string pInData, StringBuilder pOutData);
- #endregion
- #region 江苏医保刷脸DLL导入声明 (与泰和完全一致)
- /// <summary>
- /// 江苏医保刷脸功能:使用NationECCode.dll与泰和完全一致
- /// 唯一区别:URL参数格式为jiangsu_face_${biztype}
- /// </summary>
- /// <param name="strUrl">业务请求地址</param>
- /// <param name="InData">交易请求数据</param>
- /// <param name="OutData">交易返回数据</param>
- /// <returns>返回字符串指针</returns>
- [DllImport("FaceNationECCode.dll", EntryPoint = "NationEcTrans", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
- private extern static IntPtr NationEcTrans(string strUrl, string InData, ref byte OutData);
- #endregion
- #region 江苏人社标准接口DLL导入声明 (按照官方规范v2.1.1)
- /// <summary>
- /// 江苏人社标准接口:读基本信息
- /// 按照江苏省人社一体化接口规范说明书标准实现
- /// 语法:long iReadCardBas(int iType, char *pOutInfo)
- /// </summary>
- /// <param name="iType">操作卡的类型:1-接触式,2-非接触式,3-自动寻卡接触式优先,4-自动寻卡非接触式优先</param>
- /// <param name="pOutInfo">输出信息缓冲区,成功时返回卡信息,失败时返回错误描述</param>
- /// <returns>0-成功,非0-失败</returns>
- [DllImport("SSCardDriver.dll", EntryPoint = "iReadCardBas", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
- private extern static long iReadCardBas(int iType, StringBuilder pOutInfo);
- #endregion
- #region 江苏人社标准接口业务方法
- /// <summary>
- /// 江苏人社标准接口:读取社保卡基本信息
- /// 按照江苏省人社一体化接口规范说明书标准实现
- /// 使用相同的初始化方法,但接口规范不同
- /// </summary>
- /// <param name="cardType">卡类型:1-接触式,2-非接触式,3-自动寻卡接触式优先,4-自动寻卡非接触式优先</param>
- /// <returns>读卡结果</returns>
- public static JObject ReadSocialCardByStandard(int cardType = 3)
- {
- 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["interface"] = "江苏人社标准接口 v2.1.1";
- return result;
- }
- }
- // 按照规范要求分配1024字节缓冲区
- StringBuilder outInfo = new StringBuilder(1024);
- // 调用江苏人社标准接口
- long readResult = iReadCardBas(cardType, outInfo);
- if (readResult == 0)
- {
- string rawData = outInfo.ToString();
-
- // 解析标准格式的社保卡信息(以|分割的格式)
- var parsedData = ParseStandardCardInfo(rawData);
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "江苏人社标准接口读卡成功";
- result["device"] = "江苏人社标准社保卡读卡器";
- result["data"] = parsedData;
- result["cardType"] = GetCardTypeName(cardType);
- result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- result["rawData"] = rawData; // 原始数据
- result["interface"] = "江苏人社标准接口 v2.1.1";
- result["interfaceType"] = "standard"; // 标记为标准接口
-
- // 添加原始读卡数据字段(与医保接口保持一致)
- result["rawCardData"] = rawData;
- result["rawBusiData"] = ""; // 人社标准接口只返回一个数据流
- }
- else
- {
- string errorInfo = outInfo.ToString();
-
- result["success"] = false;
- result["code"] = 3000 + (int)readResult;
- result["message"] = $"江苏人社标准接口读卡失败,错误码: {readResult}";
- result["device"] = "江苏人社标准社保卡读卡器";
- result["errorCode"] = readResult;
- result["cardType"] = GetCardTypeName(cardType);
- result["rawErrorInfo"] = errorInfo; // DLL返回的原始错误信息
- result["interface"] = "江苏人社标准接口 v2.1.1";
- result["interfaceType"] = "standard"; // 标记为标准接口
- result["dllErrorMsg"] = errorInfo; // 与医保接口保持一致的字段名
-
- // 添加原始读卡数据字段(失败时为空)
- result["rawCardData"] = errorInfo;
- result["rawBusiData"] = "";
-
- // 根据错误码提供建议
- result["suggestion"] = GetErrorSuggestion(readResult);
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 3001;
- result["message"] = $"江苏人社标准接口读卡异常: {ex.Message}";
- result["device"] = "江苏人社标准社保卡读卡器";
- result["exception"] = ex.GetType().Name;
- result["interface"] = "江苏人社标准接口 v2.1.1";
- result["interfaceType"] = "standard"; // 标记为标准接口
- result["dllErrorMsg"] = $"系统异常: {ex.Message}"; // 与医保接口保持一致
-
- // 添加原始读卡数据字段(异常时为空)
- result["rawCardData"] = "";
- result["rawBusiData"] = "";
- }
- return result;
- }
- /// <summary>
- /// 解析江苏人社标准格式的社保卡信息
- /// </summary>
- /// <param name="rawData">原始数据(以|分割)</param>
- /// <returns>解析后的结构化数据</returns>
- private static JObject ParseStandardCardInfo(string rawData)
- {
- var result = new JObject();
-
- try
- {
- if (string.IsNullOrEmpty(rawData))
- {
- result["error"] = "原始数据为空";
- return result;
- }
- // 按照规范:发卡地区行政区划代码、社会保障号码、卡号、卡识别码、姓名、卡复位信息、规范版本、发卡日期、卡有效期、终端机编号、终端设备号、省人员识别号
- string[] fields = rawData.Split('|');
-
- if (fields.Length >= 12)
- {
- result["areaCode"] = fields[0]; // 发卡地区行政区划代码
- result["socialSecurityNumber"] = fields[1]; // 社会保障号码
- result["cardNumber"] = fields[2]; // 卡号
- result["cardId"] = fields[3]; // 卡识别码
- result["name"] = fields[4]; // 姓名
- result["cardResetInfo"] = fields[5]; // 卡复位信息
- result["specVersion"] = fields[6]; // 规范版本
- result["issueDate"] = FormatDate(fields[7]); // 发卡日期
- result["expireDate"] = FormatDate(fields[8]); // 卡有效期
- result["terminalNumber"] = fields[9]; // 终端机编号
- result["terminalDevice"] = fields[10]; // 终端设备号
- result["provincePersonId"] = fields[11]; // 省人员识别号
-
- result["parseStatus"] = "success";
- result["fieldCount"] = fields.Length;
- }
- else
- {
- result["error"] = $"数据字段不足,期望12个字段,实际{fields.Length}个";
- result["rawFields"] = new JArray(fields);
- result["parseStatus"] = "partial";
- }
- }
- catch (Exception ex)
- {
- result["error"] = $"解析异常: {ex.Message}";
- result["parseStatus"] = "failed";
- }
- return result;
- }
- /// <summary>
- /// 获取卡类型名称
- /// </summary>
- /// <param name="cardType">卡类型代码</param>
- /// <returns>卡类型名称</returns>
- private static string GetCardTypeName(int cardType)
- {
- switch (cardType)
- {
- case 1: return "接触式操作卡";
- case 2: return "非接触式操作卡";
- case 3: return "自动寻卡(接触式优先)";
- case 4: return "自动寻卡(非接触式优先)";
- default: return $"未知类型({cardType})";
- }
- }
- /// <summary>
- /// 根据错误码获取建议
- /// </summary>
- /// <param name="errorCode">错误码</param>
- /// <returns>错误建议</returns>
- private static string GetErrorSuggestion(long errorCode)
- {
- switch (errorCode)
- {
- case -11:
- return "请检查社保卡是否正确插入读卡器,确保卡片与读卡器接触良好";
- case -1:
- return "读卡器硬件可能未正确连接或驱动问题,请检查设备连接";
- case -2:
- return "卡片认证失败,请确认卡片为有效的社保卡";
- case -3:
- return "PSAM卡认证失败,请检查PSAM卡是否正确安装";
- default:
- return $"未知错误码 {errorCode},请联系技术支持";
- }
- }
- #endregion
- #region 配置和状态管理
- /// <summary>
- /// 刷脸认证数据结构 - 与泰和保持一致
- /// </summary>
- public class FaceAuthData
- {
- public string authNo { get; set; }
- }
- private static bool isInitialized = false;
- private static JiangSuConfig currentConfig = null;
- /// <summary>
- /// 江苏医保配置类 - 严格按照规范v0.9.9.15定义的13个必需参数(全部大写)
- /// 注意:这里写死了测试参数,生产环境请修改为实际值
- /// </summary>
- public class JiangSuConfig
- {
- /// <summary>
- /// 1. 服务端IP地址 - 写死测试值
- /// </summary>
- public string IP { get; set; } = "10.61.165.3";
- /// <summary>
- /// 2. 服务端端口 - 写死测试值
- /// </summary>
- public int PORT { get; set; } = 8086;
- /// <summary>
- /// 3. 超时时间(单位秒) - 写死测试值
- /// </summary>
- public int TIMEOUT { get; set; } = 30;
- /// <summary>
- /// 4. 动态库日志目录 - 写死测试值,确保目录存在
- /// </summary>
- public string LOG_PATH { get; set; } = "E:\\huaihaiProject\\readCard\\ThCardReader\\logs\\";
- /// <summary>
- /// 5. 电子凭证中台URL - 写死测试值
- /// </summary>
- // public string EC_URL { get; set; } = "http://10.61.165.3:8086/localcfc/api/hsecfc/localQrCodeQuery";
- public string EC_URL { get; set; } = "http://10.58.33.207:10086/localcfc/api/hsecfc/localQrCodeQuery";
- /// <summary>
- /// 6. 社保卡验证密码方式(1:验证卡pin,2:验证数据库密码) - 写死测试值
- /// 医院不需要输入密码,使用2:验证数据库密码
- /// </summary>
- public string CARD_PASSTYPE { get; set; } = "2";
- /// <summary>
- /// 7. CSB的_api_name - 写死测试值,请替换为实际申请的值
- /// </summary>
- public string API_NAME { get; set; } = "hssServives";
- /// <summary>
- /// 8. CSB的_api_version - 写死测试值
- /// </summary>
- public string API_VERSION { get; set; } = "1.0.0";
- /// <summary>
- /// 9. CSB的_api_access_key - 写死测试值,请替换为实际申请的值
- /// </summary>
- public string ACCESS_KEY { get; set; } = "090ea4c914324ee38b6978365df46a80";
- /// <summary>
- /// 10. CSB的secretKey - 写死测试值,请替换为实际申请的值
- /// </summary>
- public string SECRETKEY { get; set; } = "CMgTEMfftMP0EMMoliOs65wZmv8=";
- /// <summary>
- /// 11. 定点编号 - 写死测试值,请替换为医院实际编号
- /// </summary>
- public string ORG_ID { get; set; } = "H32132200561";
- /// <summary>
- /// 12. JSON对象字符串(预留) - 写死测试值
- /// </summary>
- public string EXT { get; set; } = "{}";
- /// <summary>
- /// 13. 定点所属行政区划代码 - 写死测试值(江苏宿迁沭阳)
- /// </summary>
- public string AREA_CODE { get; set; } = "321322";
- /// <summary>
- /// 江苏医保刷脸专用:写死的服务地址 - 与泰和完全一致的配置
- /// </summary>
- public string FACE_SERVICE_URL { get; set; } = "http://10.58.33.207:10086/localcfc/api/hsecfc/localQrCodeQuery";
- /// <summary>
- /// 江苏医保刷脸专用:写死的机构编号 - 与泰和完全一致的配置
- /// </summary>
- public string FACE_ORG_ID { get; set; } = "H32132200561";
- /// <summary>
- /// 转换为JSON字符串(严格按照规范大写参数名格式)
- /// </summary>
- public string ToJson()
- {
- var configObject = 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(configObject, Formatting.None);
- }
- }
- #endregion
- #region 核心业务方法
- /// <summary>
- /// 初始化江苏医保社保卡读取系统
- /// </summary>
- /// <param name="config">配置参数</param>
- /// <returns>初始化结果</returns>
- public static JObject Initialize(JiangSuConfig config = null)
- {
- var result = new JObject();
- try
- {
- // 先检查DLL文件并确保其在正确位置
- var dllCheckResult = EnsureDllExists();
- if (!(bool)dllCheckResult["success"])
- {
- // 如果DLL检查失败,直接返回错误
- return dllCheckResult;
- }
- // 使用默认配置或传入的配置
- if (config == null)
- {
- config = new JiangSuConfig();
- }
- // 确保日志目录存在
- if (!Directory.Exists(config.LOG_PATH))
- {
- Directory.CreateDirectory(config.LOG_PATH);
- }
- // 构造初始化JSON参数
- string initJson = config.ToJson();
- StringBuilder errMsg = new StringBuilder(2048);
- // 调用DLL初始化函数
- int initResult = Init(initJson, 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["dllErrorMsg"] = ""; // 成功时错误信息为空
- result["errMsgErrorMsg"] = errMsg.ToString(); // 输出参数pErrMsg初始化错误时候返回错误信息,成功时候返回空。
- result["initJsonMsg"] = initJson; // 初始化JSON参数
- result["initResultMsg"] = initResult; // 0表示成功;非0表示失败。
- }
- else
- {
- string errorMsg = errMsg.ToString();
- result["success"] = false;
- result["code"] = 1000 + initResult;
- result["message"] = $"江苏医保系统初始化失败: {errorMsg}";
- result["device"] = "江苏医保社保卡读卡器";
- result["errorCode"] = initResult;
- result["dllErrorMsg"] = errorMsg; // 原始DLL错误信息
- result["errMsgErrorMsg"] = errMsg.ToString(); // 输出参数pErrMsg初始化错误时候返回错误信息,成功时候返回空。
- result["initJsonMsg"] = initJson; // 初始化JSON参数
- result["initResultMsg"] = initResult; // 0表示成功;非0表示失败。
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 1001;
- result["message"] = $"江苏医保系统初始化异常: {ex.Message}";
- result["device"] = "江苏医保社保卡读卡器";
- result["exception"] = ex.GetType().Name;
- result["dllErrorMsg"] = $"系统异常: {ex.Message}"; // 异常时的错误信息
- }
- return result;
- }
- /// <summary>
- /// 读取社保卡基本信息
- /// 返回标准38号文格式数据
- /// </summary>
- /// <param name="autoVerifyPIN">是否自动验证PIN码</param>
- /// <param name="autoInit">是否自动初始化(默认false保持向后兼容)</param>
- /// <returns>读卡结果</returns>
- public static JObject ReadSocialCard(bool autoVerifyPIN = false, bool autoInit = false)
- {
- var result = new JObject();
- try
- {
- // 检查初始化状态
- if (!isInitialized)
- {
- if (autoInit)
- {
- // 智能初始化模式:自动使用默认配置初始化
- var autoInitResult = Initialize();
- if (!(bool)autoInitResult["success"])
- {
- result["success"] = false;
- result["code"] = 1002;
- result["message"] = "江苏医保系统自动初始化失败,请检查设备连接";
- result["device"] = "江苏医保社保卡读卡器";
- result["autoInitError"] = autoInitResult["message"];
- result["interface"] = "江苏医保接口 v0.9.9.15";
- result["interfaceType"] = "medical";
- result["initMode"] = "auto";
- return result;
- }
- else
- {
- // 自动初始化成功,记录日志
- result["autoInitSuccess"] = true;
- result["initMode"] = "auto";
- }
- }
- else
- {
- // 严格模式:保持原有行为,要求用户先初始化
- result["success"] = false;
- result["code"] = 1002;
- result["message"] = "江苏医保系统未初始化,请先调用初始化接口";
- result["device"] = "江苏医保社保卡读卡器";
- result["interface"] = "江苏医保接口 v0.9.9.15";
- result["interfaceType"] = "medical";
- result["initMode"] = "strict";
- result["suggestion"] = "请先调用 Initialize() 方法或设置 autoInit=true 启用自动初始化";
- return result;
- }
- }
- // 如果需要自动验证PIN码
- if (autoVerifyPIN)
- {
- var pinResult = VerifyCardPIN();
- if (!(bool)pinResult["success"])
- {
- return pinResult; // 返回PIN验证失败结果
- }
- }
- // 读取社保卡信息 - 按规范要求分配内存
- StringBuilder cardInfo = new StringBuilder(2048); // 基本信息缓冲区
- StringBuilder busiCardInfo = new StringBuilder(8192); // 业务信息缓冲区
- int readResult = ReadCardBas(cardInfo, busiCardInfo);
- if (readResult == 0)
- {
- string rawCardData = cardInfo.ToString();
- string rawBusiData = busiCardInfo.ToString();
- // 解析38号文格式数据
- var parsedData = Parse38DocumentFormat(rawCardData, rawBusiData);
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "社保卡读取成功";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = parsedData;
- result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- result["interface"] = "江苏医保接口 v0.9.9.15";
- result["interfaceType"] = "medical"; // 标记为医保接口
- result["initMode"] = autoInit ? "auto" : "manual"; // 记录初始化模式
-
- // 添加原始读卡数据
- result["rawCardData"] = rawCardData;
- result["rawBusiData"] = rawBusiData;
- }
- else
- {
- result["success"] = false;
- result["code"] = 2000 + readResult;
- result["message"] = $"社保卡读取失败,错误码: {readResult}";
- result["device"] = "江苏医保社保卡读卡器";
- result["errorCode"] = readResult;
- result["interface"] = "江苏医保接口 v0.9.9.15";
- result["interfaceType"] = "medical"; // 标记为医保接口
- result["initMode"] = autoInit ? "auto" : "manual"; // 记录初始化模式
-
- // 读卡失败时也记录原始数据(可能为空)
- result["rawCardData"] = cardInfo.ToString();
- result["rawBusiData"] = busiCardInfo.ToString();
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 2001;
- result["message"] = $"社保卡读取异常: {ex.Message}";
- result["device"] = "江苏医保社保卡读卡器";
- result["exception"] = ex.GetType().Name;
- result["interface"] = "江苏医保接口 v0.9.9.15";
- result["interfaceType"] = "medical"; // 标记为医保接口
- result["initMode"] = autoInit ? "auto" : "manual"; // 记录初始化模式
- }
- return result;
- }
- /// <summary>
- /// 验证社保卡PIN码
- /// </summary>
- /// <returns>验证结果</returns>
- public static JObject VerifyCardPIN()
- {
- var result = new JObject();
- try
- {
- if (!isInitialized)
- {
- result["success"] = false;
- result["code"] = 1002;
- result["message"] = "江苏医保系统未初始化";
- return result;
- }
- StringBuilder outBuff = new StringBuilder(1024);
- int verifyResult = VerifyPIN(outBuff);
- if (verifyResult == 0)
- {
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "PIN码验证成功";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = outBuff.ToString();
- }
- else
- {
- result["success"] = false;
- result["code"] = 3000 + verifyResult;
- result["message"] = $"PIN码验证失败,错误码: {verifyResult}";
- result["errorCode"] = verifyResult;
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 3001;
- result["message"] = $"PIN码验证异常: {ex.Message}";
- result["exception"] = ex.GetType().Name;
- }
- return result;
- }
- /// <summary>
- /// 修改社保卡PIN码
- /// </summary>
- /// <returns>修改结果</returns>
- public static JObject ChangeCardPIN()
- {
- var result = new JObject();
- try
- {
- if (!isInitialized)
- {
- result["success"] = false;
- result["code"] = 1002;
- result["message"] = "江苏医保系统未初始化";
- return result;
- }
- StringBuilder outBuff = new StringBuilder(1024);
- int changeResult = ChangePIN(outBuff);
- if (changeResult == 0)
- {
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "PIN码修改成功";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = outBuff.ToString();
- }
- else
- {
- result["success"] = false;
- result["code"] = 4000 + changeResult;
- result["message"] = $"PIN码修改失败,错误码: {changeResult}";
- result["errorCode"] = changeResult;
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 4001;
- result["message"] = $"PIN码修改异常: {ex.Message}";
- result["exception"] = ex.GetType().Name;
- }
- return result;
- }
- /// <summary>
- /// 四合一介质获取个人信息
- /// 支持社保卡、电子凭证、电子社保卡、身份证
- /// </summary>
- /// <param name="mediaType">介质类型</param>
- /// <param name="inputData">输入数据</param>
- /// <returns>个人信息</returns>
- public static JObject GetPersonInfo(string mediaType = "socialcard", string inputData = "")
- {
- var result = new JObject();
- try
- {
- if (!isInitialized)
- {
- result["success"] = false;
- result["code"] = 1002;
- result["message"] = "江苏医保系统未初始化";
- return result;
- }
- // 构造输入参数JSON
- var inputObject = new
- {
- mediaType = mediaType,
- inputData = inputData
- };
- string inputJson = JsonConvert.SerializeObject(inputObject);
- StringBuilder outData = new StringBuilder(8192);
- int getResult = GetPersonInfo(inputJson, outData);
- if (getResult == 0)
- {
- string responseData = outData.ToString();
- var parsedInfo = ParsePersonInfo(responseData, mediaType);
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "个人信息获取成功";
- result["device"] = "江苏医保社保卡读卡器";
- result["mediaType"] = mediaType;
- result["data"] = parsedInfo;
- }
- else
- {
- result["success"] = false;
- result["code"] = 5000 + getResult;
- result["message"] = $"个人信息获取失败,错误码: {getResult}";
- result["errorCode"] = getResult;
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 5001;
- result["message"] = $"个人信息获取异常: {ex.Message}";
- result["exception"] = ex.GetType().Name;
- }
- return result;
- }
- /// <summary>
- /// 获取设备状态
- /// </summary>
- /// <returns>设备状态信息</returns>
- public static JObject GetDeviceStatus()
- {
- var result = new JObject();
- try
- {
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "设备状态获取成功";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = new JObject
- {
- ["initialized"] = isInitialized,
- ["configLoaded"] = currentConfig != null,
- ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
- };
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 7001;
- result["message"] = $"设备状态获取异常: {ex.Message}";
- }
- return result;
- }
- /// <summary>
- /// 重置系统内部状态
- /// </summary>
- /// <returns>重置结果</returns>
- public static JObject ResetSystemState()
- {
- var result = new JObject();
- try
- {
- // 重置内部状态(不调用DLL函数)
- isInitialized = false;
- currentConfig = null;
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "系统状态重置成功";
- result["device"] = "江苏医保社保卡读卡器";
- result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 8001;
- result["message"] = $"系统状态重置异常: {ex.Message}";
- }
- return result;
- }
- /// <summary>
- /// 确保DLL文件在正确位置
- /// </summary>
- /// <returns>操作结果</returns>
- private static JObject EnsureDllExists()
- {
- var result = new JObject();
- try
- {
- string programDir = AppDomain.CurrentDomain.BaseDirectory;
- string targetDllPath = Path.Combine(programDir, "HeaSecReadInfo.dll");
- // 如果目标位置已经有DLL文件,直接返回成功
- if (File.Exists(targetDllPath))
- {
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "HeaSecReadInfo.dll文件已就绪";
- result["device"] = "江苏医保社保卡读卡器";
- return result;
- }
- // 尝试从可能的位置复制DLL文件
- string[] possiblePaths = {
- // 当前目录及其子目录
- Path.Combine(programDir, "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "bin", "Debug", "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "bin", "Release", "HeaSecReadInfo.dll"),
-
- // 上级目录搜索
- Path.Combine(programDir, "..", "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "..", "bin", "Debug", "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "..", "bin", "Release", "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "..", "..", "bin", "Debug", "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "..", "..", "bin", "Release", "HeaSecReadInfo.dll"),
-
- // ThCardReader项目目录
- Path.Combine(programDir, "..", "..", "ThCardReader", "bin", "Debug", "HeaSecReadInfo.dll"),
- Path.Combine(programDir, "..", "..", "ThCardReader", "bin", "Release", "HeaSecReadInfo.dll"),
-
- // 当前工作目录
- Path.Combine(Directory.GetCurrentDirectory(), "HeaSecReadInfo.dll"),
- Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "HeaSecReadInfo.dll"),
- Path.Combine(Directory.GetCurrentDirectory(), "bin", "Release", "HeaSecReadInfo.dll"),
-
- // 绝对路径搜索(根据错误信息中的路径)
- @"E:\huaihaiProject\readCard\ThCardReader\ThCardReader\bin\Debug\HeaSecReadInfo.dll",
- @"E:\huaihaiProject\readCard\ThCardReader\bin\Debug\HeaSecReadInfo.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);
-
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "HeaSecReadInfo.dll文件已复制到位";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = new JObject
- {
- ["sourcePath"] = sourcePath,
- ["targetPath"] = targetDllPath
- };
- }
- else
- {
- result["success"] = false;
- result["code"] = 8001;
- result["message"] = "找不到HeaSecReadInfo.dll文件";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = new JObject
- {
- ["searchedPaths"] = string.Join("; ", possiblePaths),
- ["programDirectory"] = programDir,
- ["suggestion"] = "请确保HeaSecReadInfo.dll文件在程序目录或bin/Debug目录中"
- };
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 8002;
- result["message"] = $"DLL文件检查异常: {ex.Message}";
- result["device"] = "江苏医保社保卡读卡器";
- result["exception"] = ex.GetType().Name;
- }
- return result;
- }
- /// <summary>
- /// 检查DLL文件是否存在并可访问
- /// </summary>
- /// <returns>检查结果</returns>
- public static JObject CheckDllExists()
- {
- var result = new JObject();
- try
- {
- // 检查程序目录下的DLL文件
- string programDir = AppDomain.CurrentDomain.BaseDirectory;
- string dllPath = Path.Combine(programDir, "HeaSecReadInfo.dll");
- if (File.Exists(dllPath))
- {
- FileInfo fileInfo = new FileInfo(dllPath);
-
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "HeaSecReadInfo.dll文件检查通过";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = new JObject
- {
- ["dllPath"] = dllPath,
- ["fileSize"] = fileInfo.Length,
- ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss"),
- ["programDirectory"] = programDir
- };
- }
- else
- {
- result["success"] = false;
- result["code"] = 8001;
- result["message"] = "HeaSecReadInfo.dll文件不存在";
- result["device"] = "江苏医保社保卡读卡器";
- result["data"] = new JObject
- {
- ["expectedPath"] = dllPath,
- ["programDirectory"] = programDir,
- ["suggestion"] = "请将HeaSecReadInfo.dll文件复制到程序目录"
- };
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 8002;
- result["message"] = $"DLL文件检查异常: {ex.Message}";
- result["device"] = "江苏医保社保卡读卡器";
- result["exception"] = ex.GetType().Name;
- }
- return result;
- }
- /// <summary>
- /// 诊断读卡器硬件和社保卡状态
- /// </summary>
- /// <returns>诊断结果</returns>
- public static JObject DiagnoseCardReader()
- {
- var result = new JObject();
-
- try
- {
- result["device"] = "江苏医保社保卡读卡器";
- result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-
- var diagnostics = new JObject();
-
- // 1. 检查DLL状态
- var dllCheck = CheckDllExists();
- diagnostics["dllStatus"] = dllCheck;
-
- // 2. 检查初始化状态
- diagnostics["initStatus"] = new JObject
- {
- ["isInitialized"] = isInitialized,
- ["hasConfig"] = currentConfig != null,
- ["configDetails"] = currentConfig?.ToJson() ?? "未配置"
- };
-
- // 3. 尝试不同的读卡配置
- var cardTests = new JObject();
-
- // 测试无PIN模式
- try
- {
- var config = new JiangSuConfig();
- config.CARD_PASSTYPE = "2"; // 验证数据库密码(跳过PIN)
-
- var initResult = Initialize(config);
- var readResult = ReadSocialCard(false); // 不验证PIN
-
- cardTests["noPinTest"] = new JObject
- {
- ["config"] = "CARD_PASSTYPE=2(跳过PIN验证)",
- ["initResult"] = initResult,
- ["readResult"] = readResult
- };
- }
- catch (Exception ex)
- {
- cardTests["noPinTest"] = new JObject
- {
- ["config"] = "CARD_PASSTYPE=2(跳过PIN验证)",
- ["error"] = ex.Message
- };
- }
-
- // 测试PIN模式
- try
- {
- var config = new JiangSuConfig();
- config.CARD_PASSTYPE = "1"; // 验证卡PIN
-
- var initResult = Initialize(config);
- var readResult = ReadSocialCard(false); // 不自动验证PIN
-
- cardTests["pinTest"] = new JObject
- {
- ["config"] = "CARD_PASSTYPE=1(需要PIN验证)",
- ["initResult"] = initResult,
- ["readResult"] = readResult
- };
- }
- catch (Exception ex)
- {
- cardTests["pinTest"] = new JObject
- {
- ["config"] = "CARD_PASSTYPE=1(需要PIN验证)",
- ["error"] = ex.Message
- };
- }
-
- diagnostics["cardTests"] = cardTests;
-
- // 4. 分析结果
- var analysis = new JObject();
- analysis["recommendations"] = new JArray();
-
- // 检查是否有成功的读卡
- bool hasSuccessfulRead = false;
- if (cardTests["noPinTest"] != null && cardTests["noPinTest"]["readResult"] != null)
- {
- var noPinResult = (JObject)cardTests["noPinTest"]["readResult"];
- if (noPinResult["success"] != null && (bool)noPinResult["success"])
- {
- hasSuccessfulRead = true;
- ((JArray)analysis["recommendations"]).Add("✅ 使用CARD_PASSTYPE=2可以成功读卡");
- }
- else if (noPinResult["errorCode"] != null)
- {
- int errorCode = (int)noPinResult["errorCode"];
- if (errorCode == -11)
- {
- ((JArray)analysis["recommendations"]).Add("⚠️ 错误码-11:请检查社保卡是否正确插入并接触良好");
- }
- }
- }
-
- if (cardTests["pinTest"] != null && cardTests["pinTest"]["readResult"] != null)
- {
- var pinResult = (JObject)cardTests["pinTest"]["readResult"];
- if (pinResult["success"] != null && (bool)pinResult["success"])
- {
- hasSuccessfulRead = true;
- ((JArray)analysis["recommendations"]).Add("✅ 使用CARD_PASSTYPE=1可以成功读卡");
- }
- }
-
- if (!hasSuccessfulRead)
- {
- ((JArray)analysis["recommendations"]).Add("❌ 两种模式都无法读卡,可能的原因:");
- ((JArray)analysis["recommendations"]).Add(" 1. 社保卡未正确插入或损坏");
- ((JArray)analysis["recommendations"]).Add(" 2. 读卡器硬件故障或驱动问题");
- ((JArray)analysis["recommendations"]).Add(" 3. 网络配置参数错误");
- ((JArray)analysis["recommendations"]).Add(" 4. DLL文件版本不匹配");
- }
-
- analysis["overallStatus"] = hasSuccessfulRead ? "正常" : "异常";
- diagnostics["analysis"] = analysis;
-
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "读卡器诊断完成";
- result["data"] = diagnostics;
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 9001;
- result["message"] = $"读卡器诊断异常: {ex.Message}";
- result["device"] = "江苏医保社保卡读卡器";
- result["exception"] = ex.GetType().Name;
- }
-
- return result;
- }
- /// <summary>
- /// 离线模式读取社保卡(跳过网络验证,仅测试本地读卡功能)
- /// </summary>
- /// <returns>读卡结果</returns>
- public static JObject ReadSocialCardOffline()
- {
- var result = new JObject();
- try
- {
- // 使用最简配置进行本地测试
- var offlineConfig = new JiangSuConfig();
- offlineConfig.IP = "127.0.0.1"; // 本地IP
- offlineConfig.PORT = 8080; // 本地端口
- offlineConfig.EC_URL = ""; // 空URL跳过网络验证
- offlineConfig.ACCESS_KEY = ""; // 空密钥
- offlineConfig.SECRETKEY = ""; // 空密钥
- // 构造最简初始化参数
- var simpleConfig = new
- {
- IP = "127.0.0.1",
- PORT = 8080,
- TIMEOUT = 5,
- LOG_PATH = offlineConfig.LOG_PATH,
- CARD_PASSTYPE = "2", // 跳过PIN验证
- EC_URL = "",
- API_NAME = "",
- API_VERSION = "",
- ACCESS_KEY = "",
- SECRETKEY = "",
- ORG_ID = "",
- EXT = "{}",
- AREA_CODE = ""
- };
- string initJson = JsonConvert.SerializeObject(simpleConfig, Formatting.None);
- StringBuilder errMsg = new StringBuilder(2048);
- // 尝试离线初始化
- int initResult = Init(initJson, errMsg);
- if (initResult == 0)
- {
- // 初始化成功,尝试读卡
- isInitialized = true;
- currentConfig = offlineConfig;
- StringBuilder cardInfo = new StringBuilder(2048);
- StringBuilder busiCardInfo = new StringBuilder(8192);
- int readResult = ReadCardBas(cardInfo, busiCardInfo);
- if (readResult == 0)
- {
- string rawCardData = cardInfo.ToString();
- string rawBusiData = busiCardInfo.ToString();
- var parsedData = Parse38DocumentFormat(rawCardData, rawBusiData);
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "离线模式读卡成功";
- result["device"] = "江苏医保社保卡读卡器(离线模式)";
- result["data"] = parsedData;
- result["mode"] = "offline";
- result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- result["dllErrorMsg"] = ""; // 成功时错误信息为空
-
- // 添加原始读卡数据
- result["rawCardData"] = rawCardData;
- result["rawBusiData"] = rawBusiData;
- }
- else
- {
- result["success"] = false;
- result["code"] = 2000 + readResult;
- result["message"] = $"离线模式读卡失败,错误码: {readResult}";
- result["device"] = "江苏医保社保卡读卡器(离线模式)";
- result["mode"] = "offline";
- result["errorCode"] = readResult;
- result["dllErrorMsg"] = ""; // 读卡失败,但初始化成功
-
- // 读卡失败时也记录原始数据(可能为空)
- result["rawCardData"] = cardInfo.ToString();
- result["rawBusiData"] = busiCardInfo.ToString();
- if (readResult == -11)
- {
- result["suggestion"] = "请检查社保卡是否正确插入读卡器";
- }
- else if (readResult == -1)
- {
- result["suggestion"] = "读卡器硬件可能未正确连接或驱动问题";
- }
- }
- }
- else
- {
- string errorMsg = errMsg.ToString();
- result["success"] = false;
- result["code"] = 1000 + initResult;
- result["message"] = $"离线模式初始化失败: {errorMsg}";
- result["device"] = "江苏医保社保卡读卡器(离线模式)";
- result["mode"] = "offline";
- result["errorCode"] = initResult;
- result["dllErrorMsg"] = errorMsg; // 原始DLL错误信息
- }
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 9999;
- result["message"] = $"离线模式异常: {ex.Message}";
- result["device"] = "江苏医保社保卡读卡器(离线模式)";
- result["mode"] = "offline";
- result["exception"] = ex.GetType().Name;
- result["dllErrorMsg"] = $"离线模式异常: {ex.Message}"; // 异常时的错误信息
- }
- return result;
- }
- /// <summary>
- /// 测试不同网络配置对初始化的影响
- /// </summary>
- /// <returns>测试结果</returns>
- public static JObject TestNetworkConfigurations()
- {
- var result = new JObject();
- var tests = new JObject();
-
- try
- {
- // 测试1:使用原始配置(10.61.165.3:8086)
- var config1 = new JiangSuConfig();
- config1.IP = "10.61.165.3";
- config1.PORT = 8086;
- config1.EC_URL = "http://10.61.165.3:8086/localcfc/api/hsecfc/localQrCodeQuery";
-
- var test1 = TestSingleConfig(config1, "原始配置(10.61.165.3:8086)");
- tests["originalConfig"] = test1;
-
- // 测试2:使用HSM配置(10.79.157.29:8089)
- var config2 = new JiangSuConfig();
- config2.IP = "10.79.157.29";
- config2.PORT = 8089;
- config2.EC_URL = "http://10.79.157.29:8089/encryptionMachine/encryptionMachineBusiness";
-
- var test2 = TestSingleConfig(config2, "HSM配置(10.79.157.29:8089)");
- tests["hsmConfig"] = test2;
-
- // 测试3:使用无效配置
- var config3 = new JiangSuConfig();
- config3.IP = "192.168.1.1";
- config3.PORT = 9999;
- config3.EC_URL = "http://192.168.1.1:9999/invalid";
-
- var test3 = TestSingleConfig(config3, "无效配置(192.168.1.1:9999)");
- tests["invalidConfig"] = test3;
-
- // 测试4:空配置
- var config4 = new JiangSuConfig();
- config4.IP = "";
- config4.PORT = 0;
- config4.EC_URL = "";
- config4.ACCESS_KEY = "";
- config4.SECRETKEY = "";
-
- var test4 = TestSingleConfig(config4, "空配置");
- tests["emptyConfig"] = test4;
-
- result["success"] = true;
- result["code"] = 200;
- result["message"] = "网络配置测试完成";
- result["tests"] = tests;
- result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-
- // 分析结果
- var analysis = new JArray();
- foreach (var test in tests)
- {
- var testResult = (JObject)test.Value;
- var errorCode = testResult["errorCode"]?.ToString() ?? "无";
- var config = testResult["configName"]?.ToString() ?? "未知";
- analysis.Add($"{config}: 错误码 {errorCode}");
- }
- result["analysis"] = analysis;
- }
- catch (Exception ex)
- {
- result["success"] = false;
- result["code"] = 9001;
- result["message"] = $"网络配置测试异常: {ex.Message}";
- result["exception"] = ex.GetType().Name;
- }
-
- return result;
- }
-
- /// <summary>
- /// 测试单个配置
- /// </summary>
- private static JObject TestSingleConfig(JiangSuConfig config, string configName)
- {
- var testResult = new JObject();
- testResult["configName"] = configName;
- testResult["config"] = JObject.Parse(config.ToJson());
-
- try
- {
- string initJson = config.ToJson();
- StringBuilder errMsg = new StringBuilder(2048);
-
- int initResult = Init(initJson, errMsg);
-
- testResult["initResult"] = initResult;
- testResult["errorMessage"] = errMsg.ToString();
- testResult["errorCode"] = initResult;
- testResult["dllErrorMsg"] = errMsg.ToString(); // 原始DLL错误信息
-
- if (initResult == 0)
- {
- testResult["status"] = "初始化成功";
-
- // 如果初始化成功,尝试读卡
- StringBuilder cardInfo = new StringBuilder(2048);
- StringBuilder busiCardInfo = new StringBuilder(8192);
-
- int readResult = ReadCardBas(cardInfo, busiCardInfo);
- testResult["readResult"] = readResult;
- testResult["readStatus"] = readResult == 0 ? "读卡成功" : $"读卡失败,错误码: {readResult}";
- }
- else
- {
- testResult["status"] = $"初始化失败,错误码: {initResult}";
- }
- }
- catch (Exception ex)
- {
- testResult["status"] = "测试异常";
- testResult["error"] = ex.Message;
- testResult["errorCode"] = -999;
- testResult["dllErrorMsg"] = $"测试异常: {ex.Message}"; // 异常时的错误信息
- }
-
- return testResult;
- }
- #endregion
- #region 电子凭证解码功能 (按照江苏医保接口规范v0.9.9.15实现)
- /// <summary>
- /// 江苏医保电子凭证解码
- /// 严格按照《医疗保障信息平台定点基线版医药机构接口规范 v0.9.9.15》中的1.14.6电子凭证解码接口实现
- /// </summary>
- /// <param name="businessType">用码业务类型,如:01101-门诊挂号,01301-门诊结算,02121-药店购药</param>
- /// <param name="operatorId">收款员编号</param>
- /// <param name="operatorName">收款员姓名</param>
- /// <param name="officeId">医保科室编号</param>
- /// <param name="officeName">科室名称</param>
- /// <returns>电子凭证解码结果</returns>
- public static JObject ReadElectronicCertificate(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";
-
- // 添加兼容字段(与前端保持一致,采用泰和医院格式)
- result["readCardResult"] = JsonConvert.SerializeObject(dllResponseFormat);
- }
- 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;
- }
- /// <summary>
- /// 根据业务场景获取科室名称
- /// </summary>
- /// <param name="businessType">业务类型代码</param>
- /// <returns>科室名称</returns>
- private static string GetDepartmentNameByBusinessType(string businessType)
- {
- switch (businessType)
- {
- case "01101": // 门诊挂号
- return "挂号处";
- case "01201": // 门诊问诊
- case "01202": // 预约检查
- case "01203": // 检查
- case "01204": // 治疗
- return "门诊科室";
- case "01301": // 门诊结算
- return "门诊收费处";
- case "01302": // 取药
- return "门诊药房";
- case "01102": // 住院建档
- case "01103": // 入院登记
- case "01104": // 缴纳预缴金
- return "住院处";
- case "02121": // 药店购药
- case "02122": // 下载外购处方
- return "药店";
- default:
- return "医保科";
- }
- }
- /// <summary>
- /// 获取EcQuery错误码对应的建议
- /// </summary>
- /// <param name="errorCode">错误码</param>
- /// <returns>错误建议</returns>
- private static string GetEcQueryErrorSuggestion(int errorCode)
- {
- switch (errorCode)
- {
- case -1:
- return "网络连接失败,请检查网络连接和医保平台服务状态";
- case -2:
- return "身份认证失败,请检查ACCESS_KEY和SECRET_KEY配置";
- case -3:
- return "参数格式错误,请检查业务类型和定点编号配置";
- case -4:
- return "电子凭证已过期,请提示患者刷新电子凭证";
- case -5:
- return "参保地不匹配,请确认患者参保地信息";
- default:
- return "电子凭证解码失败,请检查设备连接和配置信息";
- }
- }
- /// <summary>
- /// 验证业务类型代码是否有效
- /// </summary>
- /// <param name="businessType">业务类型代码</param>
- /// <returns>是否有效</returns>
- 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", // 结算、取药、取报告、打印票据和清单、病历材料复印
- // 药店业务类型
- "02121", "02122" // 药店购药、下载外购处方
- };
- return validTypes.Contains(businessType);
- }
- #endregion
- #region 数据解析方法
- /// <summary>
- /// 解析38号文格式的社保卡数据
- /// </summary>
- /// <param name="cardData">卡基本信息</param>
- /// <param name="busiData">业务信息</param>
- /// <returns>解析后的数据</returns>
- private static JObject Parse38DocumentFormat(string cardData, string busiData)
- {
- var result = new JObject();
- try
- {
- // 按照38号文格式解析,使用管道符"|"分隔
- string[] cardFields = cardData.Split('|');
-
- if (cardFields.Length >= 11)
- {
- result["areaCode"] = cardFields[0]; // 发卡地区行政区划代码
- result["socialSecurityNumber"] = cardFields[1]; // 社会保障号码
- result["cardNumber"] = cardFields[2]; // 卡号
- result["cardId"] = cardFields[3]; // 卡识别码
- result["name"] = cardFields[4]; // 姓名
- result["cardResetInfo"] = cardFields[5]; // 卡复位信息
- result["specVersion"] = cardFields[6]; // 规范版本
- result["issueDate"] = FormatDate(cardFields[7]); // 发卡日期
- result["expireDate"] = FormatDate(cardFields[8]); // 卡有效期
- result["terminalNumber"] = cardFields[9]; // 终端机编号
- result["terminalDevice"] = cardFields[10]; // 终端设备号
- }
- // 解析业务信息(如果有)
- if (!string.IsNullOrEmpty(busiData))
- {
- result["businessInfo"] = busiData;
- }
- result["format"] = "38号文标准格式";
- result["parseTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- }
- catch (Exception ex)
- {
- result["error"] = $"数据解析失败: {ex.Message}";
- }
- return result;
- }
- /// <summary>
- /// 解析四合一介质的个人信息
- /// </summary>
- /// <param name="responseData">响应数据</param>
- /// <param name="mediaType">介质类型</param>
- /// <returns>解析后的个人信息</returns>
- private static JObject ParsePersonInfo(string responseData, string mediaType)
- {
- var result = new JObject();
- try
- {
- // 尝试解析JSON格式的响应
- if (responseData.StartsWith("{"))
- {
- var jsonData = JObject.Parse(responseData);
- result = jsonData;
- }
- else
- {
- // 如果不是JSON,按不同介质类型解析
- switch (mediaType.ToLower())
- {
- case "socialcard":
- // 社保卡:按38号文格式解析
- result = Parse38DocumentFormat(responseData, "");
- break;
- case "idcard":
- // 身份证:按身份证格式解析
- string[] idFields = responseData.Split('|');
- if (idFields.Length >= 8)
- {
- result["idNumber"] = idFields[0];
- result["name"] = idFields[1];
- result["sex"] = idFields[2];
- result["nation"] = idFields[3];
- result["birthday"] = FormatDate(idFields[4]);
- result["address"] = idFields[5];
- result["issueDate"] = FormatDate(idFields[6]);
- result["validDate"] = FormatDate(idFields[7]);
- }
- break;
- case "electronic":
- case "esocialcard":
- // 电子凭证/电子社保卡:通常为JSON格式
- result["rawData"] = responseData;
- break;
- default:
- result["rawData"] = responseData;
- break;
- }
- }
- result["mediaType"] = mediaType;
- result["parseTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
- }
- catch (Exception ex)
- {
- result["error"] = $"个人信息解析失败: {ex.Message}";
- result["rawData"] = responseData;
- }
- return result;
- }
- /// <summary>
- /// 格式化日期字符串
- /// </summary>
- /// <param name="dateStr">原始日期字符串</param>
- /// <returns>格式化后的日期</returns>
- private static string FormatDate(string dateStr)
- {
- if (string.IsNullOrEmpty(dateStr) || dateStr.Length < 8)
- return dateStr;
- // 尝试格式化YYYYMMDD为YYYY-MM-DD
- if (dateStr.Length == 8 && dateStr.All(char.IsDigit))
- {
- return $"{dateStr.Substring(0, 4)}-{dateStr.Substring(4, 2)}-{dateStr.Substring(6, 2)}";
- }
- return dateStr;
- }
- #endregion
- #region 江苏医保刷脸功能 (使用NationECCode.dll与泰和完全一致)
- /// <summary>
- /// 江苏医保刷脸功能 - 使用NationECCode.dll与泰和完全一致
- /// 完全相同的实现:相同的DLL、相同的URL、相同的逻辑、相同的参数格式
- /// 唯一区别:使用写死的配置参数
- /// </summary>
- /// <param name="biztype">业务类型</param>
- /// <returns>刷脸结果</returns>
- public static JObject ReadFace(string biztype)
- {
- JObject result = new JObject();
-
- try
- {
- // 使用写死的江苏医保刷脸配置参数
- if (null == currentConfig)
- {
- currentConfig = new JiangSuConfig();
- }
-
- // 第一步:获取授权码(cn.nhsa.ec.auth)
- // 补全所有参数,即使为空也带上
- var data = new {
- businessType = biztype,
- deviceType = "",
- orgId = currentConfig.FACE_ORG_ID,
- outBizNo = "",
- operatorId = "",
- operatorName = "",
- officeId = "",
- officeName = ""
- };
- var authRequest = new {
- data = data,
- orgId = currentConfig.FACE_ORG_ID,
- transType = "cn.nhsa.ec.auth"
- };
- string pindata = JsonConvert.SerializeObject(authRequest, Formatting.None);
-
- byte[] outinfo = new byte[4096];
-
- IntPtr retPtr = NationEcTrans(currentConfig.FACE_SERVICE_URL, pindata, ref outinfo[0]);
- string businessResultCode = Marshal.PtrToStringAnsi(retPtr).Trim().Replace("\u0000", "");
- string outstr = Encoding.Default.GetString(outinfo).Trim().Replace("\u0000", "");
-
- // 检查DLL调用是否成功
- if (businessResultCode != "0000")
- {
- result.Add("code", 1001);
- result.Add("message", $"DLL调用失败,返回码: {businessResultCode}");
- result.Add("type", "face");
- result.Add("device", "江苏医保刷脸认证器(NationECCode.dll)");
- result.Add("businessType", biztype);
- result.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
- result.Add("interfaceVersion", "江苏医保刷脸v1.0(与泰和完全一致)");
- result.Add("dllReturnCode", businessResultCode);
- result.Add("dllRawResponse", outstr);
- return result;
- }
-
- ReadCardResult tmp = Newtonsoft.Json.JsonConvert.DeserializeObject<ReadCardResult>(outstr);
- if (tmp.code == 0)
- {
- FaceAuthData authData = Newtonsoft.Json.JsonConvert.DeserializeObject<FaceAuthData>(tmp.data.ToString());
- return getUserInfoByAuthNo(biztype, authData.authNo);
- }
- else
- {
- result.Add("code", 1001);
- result.Add("message", tmp.message ?? "刷脸授权获取失败");
- result.Add("type", "face");
- result.Add("device", "江苏医保刷脸认证器(NationECCode.dll)");
- result.Add("businessType", biztype);
- result.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
- result.Add("interfaceVersion", "江苏医保刷脸v1.0(与泰和完全一致)");
- result.Add("dllReturnCode", businessResultCode);
- result.Add("dllRawResponse", outstr);
- result.Add("authStep", "第一步:获取授权码失败");
- return result;
- }
- }
- catch (Exception ex)
- {
- result.Add("code", 1001);
- result.Add("message", $"江苏医保刷脸操作异常: {ex.Message}");
- result.Add("type", "face");
- result.Add("device", "江苏医保刷脸认证器(NationECCode.dll)");
- result.Add("businessType", biztype);
- result.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
- result.Add("interfaceVersion", "江苏医保刷脸v1.0(与泰和完全一致)");
- result.Add("exception", ex.GetType().Name);
- result.Add("exceptionMessage", ex.Message);
- return result;
- }
- }
- /// <summary>
- /// 根据authNo获取用户信息 - 使用NationECCode.dll与泰和完全一致
- /// </summary>
- /// <param name="biztype">业务类型</param>
- /// <param name="authNo">认证号</param>
- /// <returns>用户信息</returns>
- private static JObject getUserInfoByAuthNo(string biztype, string authNo)
- {
- JObject result = new JObject();
-
- try
- {
- // 使用写死的江苏医保刷脸配置参数
- if (null == currentConfig)
- {
- currentConfig = new JiangSuConfig();
- }
-
- // 第二步:根据authNo获取医保身份(cn.nhsa.auth.check)
- // 补全所有参数,即使为空也带上
- var data = new {
- businessType = biztype,
- authNo = authNo,
- deviceType = "",
- orgId = currentConfig.FACE_ORG_ID,
- outBizNo = "",
- operatorId = "",
- operatorName = "",
- officeId = "",
- officeName = ""
- };
- var checkRequest = new {
- data = data,
- orgId = currentConfig.FACE_ORG_ID,
- transType = "cn.nhsa.auth.check"
- };
- string pindata = JsonConvert.SerializeObject(checkRequest, Formatting.None);
-
- byte[] outinfo = new byte[4096];
-
- IntPtr retPtr = NationEcTrans(currentConfig.FACE_SERVICE_URL, pindata, ref outinfo[0]);
- string businessResultCode = Marshal.PtrToStringAnsi(retPtr).Trim().Replace("\u0000", "");
- string outstr = Encoding.Default.GetString(outinfo).Trim().Replace("\u0000", "");
-
- ReadCardResult tmp = Newtonsoft.Json.JsonConvert.DeserializeObject<ReadCardResult>(outstr);
- if (tmp.code == 0)
- {
- result.Add("code", 200);
- result.Add("message", "刷脸获取医保身份成功。");
- result.Add("type", "face");
- result.Add("data", outstr);
- result.Add("sign", null);
- }
- else
- {
- result.Add("code", 1001);
- result.Add("message", tmp.message ?? "获取医保身份失败");
- }
- }
- catch (Exception ex)
- {
- result.Add("code", 1001);
- result.Add("message", $"获取医保身份异常: {ex.Message}");
- }
-
- return result;
- }
- #endregion
- }
- }
|