JiangSuSocialCardBusiness.cs 86 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995
  1. using System;
  2. using System.IO;
  3. using System.Linq;
  4. using System.Runtime.InteropServices;
  5. using System.Text;
  6. using Newtonsoft.Json;
  7. using Newtonsoft.Json.Linq;
  8. namespace ThCardReader
  9. {
  10. /// <summary>
  11. /// 江苏医保社保卡读取业务类
  12. /// 基于HeaSecReadInfo.dll作为主DLL,严格按照江苏医保规范实现
  13. /// </summary>
  14. public class JiangSuSocialCardBusiness
  15. {
  16. #region DLL导入声明
  17. // 主DLL: HeaSecReadInfo.dll - 严格按照江苏医保规范v0.9.9.15实现
  18. /// <summary>
  19. /// 1.14.1 初始化
  20. /// </summary>
  21. /// <param name="pInitInfo">JSON格式的初始化参数</param>
  22. /// <param name="pErrMsg">错误信息输出缓冲区</param>
  23. /// <returns>0-成功,其他-失败</returns>
  24. [DllImport("HeaSecReadInfo.dll", EntryPoint = "Init", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  25. private extern static Int32 Init(string pInitInfo, StringBuilder pErrMsg);
  26. /// <summary>
  27. /// 1.14.2 读社保卡基本信息
  28. /// </summary>
  29. /// <param name="pCardInfo">社保卡基本信息输出缓冲区</param>
  30. /// <param name="pBusiCardInfo">社保卡业务信息输出缓冲区</param>
  31. /// <returns>0-成功,其他-失败</returns>
  32. [DllImport("HeaSecReadInfo.dll", EntryPoint = "ReadCardBas", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  33. private extern static Int32 ReadCardBas(StringBuilder pCardInfo, StringBuilder pBusiCardInfo);
  34. /// <summary>
  35. /// 1.14.3 检验PIN码
  36. /// </summary>
  37. /// <param name="pOutBuff">输出信息缓冲区</param>
  38. /// <returns>0-成功,其他-失败</returns>
  39. [DllImport("HeaSecReadInfo.dll", EntryPoint = "VerifyPIN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  40. private extern static Int32 VerifyPIN(StringBuilder pOutBuff);
  41. /// <summary>
  42. /// 1.14.4 修改PIN码
  43. /// </summary>
  44. /// <param name="pOutBuff">输出信息缓冲区</param>
  45. /// <returns>0-成功,其他-失败</returns>
  46. [DllImport("HeaSecReadInfo.dll", EntryPoint = "ChangePIN", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  47. private extern static Int32 ChangePIN(StringBuilder pOutBuff);
  48. /// <summary>
  49. /// 1.14.6 电子凭证解码
  50. /// </summary>
  51. /// <param name="pInData">输入数据(JSON格式)</param>
  52. /// <param name="pOutData">输出数据缓冲区</param>
  53. /// <returns>0-成功,其他-失败</returns>
  54. [DllImport("HeaSecReadInfo.dll", EntryPoint = "EcQuery", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  55. private extern static Int32 EcQuery(string pInData, StringBuilder pOutData);
  56. /// <summary>
  57. /// 1.14.8 四合一介质获得个人信息
  58. /// </summary>
  59. /// <param name="pInData">输入数据</param>
  60. /// <param name="pOutData">输出数据缓冲区</param>
  61. /// <returns>0-成功,其他-失败</returns>
  62. [DllImport("HeaSecReadInfo.dll", EntryPoint = "GetPersonInfo", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  63. private extern static Int32 GetPersonInfo(string pInData, StringBuilder pOutData);
  64. #endregion
  65. #region 江苏医保刷脸DLL导入声明 (与泰和完全一致)
  66. /// <summary>
  67. /// 江苏医保刷脸功能:使用NationECCode.dll与泰和完全一致
  68. /// 唯一区别:URL参数格式为jiangsu_face_${biztype}
  69. /// </summary>
  70. /// <param name="strUrl">业务请求地址</param>
  71. /// <param name="InData">交易请求数据</param>
  72. /// <param name="OutData">交易返回数据</param>
  73. /// <returns>返回字符串指针</returns>
  74. [DllImport("FaceNationECCode.dll", EntryPoint = "NationEcTrans", CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
  75. private extern static IntPtr NationEcTrans(string strUrl, string InData, ref byte OutData);
  76. #endregion
  77. #region 江苏人社标准接口DLL导入声明 (按照官方规范v2.1.1)
  78. /// <summary>
  79. /// 江苏人社标准接口:读基本信息
  80. /// 按照江苏省人社一体化接口规范说明书标准实现
  81. /// 语法:long iReadCardBas(int iType, char *pOutInfo)
  82. /// </summary>
  83. /// <param name="iType">操作卡的类型:1-接触式,2-非接触式,3-自动寻卡接触式优先,4-自动寻卡非接触式优先</param>
  84. /// <param name="pOutInfo">输出信息缓冲区,成功时返回卡信息,失败时返回错误描述</param>
  85. /// <returns>0-成功,非0-失败</returns>
  86. [DllImport("SSCardDriver.dll", EntryPoint = "iReadCardBas", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
  87. private extern static long iReadCardBas(int iType, StringBuilder pOutInfo);
  88. #endregion
  89. #region 江苏人社标准接口业务方法
  90. /// <summary>
  91. /// 江苏人社标准接口:读取社保卡基本信息
  92. /// 按照江苏省人社一体化接口规范说明书标准实现
  93. /// 使用相同的初始化方法,但接口规范不同
  94. /// </summary>
  95. /// <param name="cardType">卡类型:1-接触式,2-非接触式,3-自动寻卡接触式优先,4-自动寻卡非接触式优先</param>
  96. /// <returns>读卡结果</returns>
  97. public static JObject ReadSocialCardByStandard(int cardType = 3)
  98. {
  99. var result = new JObject();
  100. try
  101. {
  102. // 检查初始化状态 - 使用相同的初始化检查
  103. if (!isInitialized)
  104. {
  105. // 如果未初始化,自动使用默认配置初始化
  106. var autoInitResult = Initialize();
  107. if (!(bool)autoInitResult["success"])
  108. {
  109. result["success"] = false;
  110. result["code"] = 1002;
  111. result["message"] = "江苏人社系统自动初始化失败,请检查设备连接";
  112. result["device"] = "江苏人社标准社保卡读卡器";
  113. result["autoInitError"] = autoInitResult["message"];
  114. result["interface"] = "江苏人社标准接口 v2.1.1";
  115. return result;
  116. }
  117. }
  118. // 按照规范要求分配1024字节缓冲区
  119. StringBuilder outInfo = new StringBuilder(1024);
  120. // 调用江苏人社标准接口
  121. long readResult = iReadCardBas(cardType, outInfo);
  122. if (readResult == 0)
  123. {
  124. string rawData = outInfo.ToString();
  125. // 解析标准格式的社保卡信息(以|分割的格式)
  126. var parsedData = ParseStandardCardInfo(rawData);
  127. result["success"] = true;
  128. result["code"] = 200;
  129. result["message"] = "江苏人社标准接口读卡成功";
  130. result["device"] = "江苏人社标准社保卡读卡器";
  131. result["data"] = parsedData;
  132. result["cardType"] = GetCardTypeName(cardType);
  133. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  134. result["rawData"] = rawData; // 原始数据
  135. result["interface"] = "江苏人社标准接口 v2.1.1";
  136. result["interfaceType"] = "standard"; // 标记为标准接口
  137. // 添加原始读卡数据字段(与医保接口保持一致)
  138. result["rawCardData"] = rawData;
  139. result["rawBusiData"] = ""; // 人社标准接口只返回一个数据流
  140. }
  141. else
  142. {
  143. string errorInfo = outInfo.ToString();
  144. result["success"] = false;
  145. result["code"] = 3000 + (int)readResult;
  146. result["message"] = $"江苏人社标准接口读卡失败,错误码: {readResult}";
  147. result["device"] = "江苏人社标准社保卡读卡器";
  148. result["errorCode"] = readResult;
  149. result["cardType"] = GetCardTypeName(cardType);
  150. result["rawErrorInfo"] = errorInfo; // DLL返回的原始错误信息
  151. result["interface"] = "江苏人社标准接口 v2.1.1";
  152. result["interfaceType"] = "standard"; // 标记为标准接口
  153. result["dllErrorMsg"] = errorInfo; // 与医保接口保持一致的字段名
  154. // 添加原始读卡数据字段(失败时为空)
  155. result["rawCardData"] = errorInfo;
  156. result["rawBusiData"] = "";
  157. // 根据错误码提供建议
  158. result["suggestion"] = GetErrorSuggestion(readResult);
  159. }
  160. }
  161. catch (Exception ex)
  162. {
  163. result["success"] = false;
  164. result["code"] = 3001;
  165. result["message"] = $"江苏人社标准接口读卡异常: {ex.Message}";
  166. result["device"] = "江苏人社标准社保卡读卡器";
  167. result["exception"] = ex.GetType().Name;
  168. result["interface"] = "江苏人社标准接口 v2.1.1";
  169. result["interfaceType"] = "standard"; // 标记为标准接口
  170. result["dllErrorMsg"] = $"系统异常: {ex.Message}"; // 与医保接口保持一致
  171. // 添加原始读卡数据字段(异常时为空)
  172. result["rawCardData"] = "";
  173. result["rawBusiData"] = "";
  174. }
  175. return result;
  176. }
  177. /// <summary>
  178. /// 解析江苏人社标准格式的社保卡信息
  179. /// </summary>
  180. /// <param name="rawData">原始数据(以|分割)</param>
  181. /// <returns>解析后的结构化数据</returns>
  182. private static JObject ParseStandardCardInfo(string rawData)
  183. {
  184. var result = new JObject();
  185. try
  186. {
  187. if (string.IsNullOrEmpty(rawData))
  188. {
  189. result["error"] = "原始数据为空";
  190. return result;
  191. }
  192. // 按照规范:发卡地区行政区划代码、社会保障号码、卡号、卡识别码、姓名、卡复位信息、规范版本、发卡日期、卡有效期、终端机编号、终端设备号、省人员识别号
  193. string[] fields = rawData.Split('|');
  194. if (fields.Length >= 12)
  195. {
  196. result["areaCode"] = fields[0]; // 发卡地区行政区划代码
  197. result["socialSecurityNumber"] = fields[1]; // 社会保障号码
  198. result["cardNumber"] = fields[2]; // 卡号
  199. result["cardId"] = fields[3]; // 卡识别码
  200. result["name"] = fields[4]; // 姓名
  201. result["cardResetInfo"] = fields[5]; // 卡复位信息
  202. result["specVersion"] = fields[6]; // 规范版本
  203. result["issueDate"] = FormatDate(fields[7]); // 发卡日期
  204. result["expireDate"] = FormatDate(fields[8]); // 卡有效期
  205. result["terminalNumber"] = fields[9]; // 终端机编号
  206. result["terminalDevice"] = fields[10]; // 终端设备号
  207. result["provincePersonId"] = fields[11]; // 省人员识别号
  208. result["parseStatus"] = "success";
  209. result["fieldCount"] = fields.Length;
  210. }
  211. else
  212. {
  213. result["error"] = $"数据字段不足,期望12个字段,实际{fields.Length}个";
  214. result["rawFields"] = new JArray(fields);
  215. result["parseStatus"] = "partial";
  216. }
  217. }
  218. catch (Exception ex)
  219. {
  220. result["error"] = $"解析异常: {ex.Message}";
  221. result["parseStatus"] = "failed";
  222. }
  223. return result;
  224. }
  225. /// <summary>
  226. /// 获取卡类型名称
  227. /// </summary>
  228. /// <param name="cardType">卡类型代码</param>
  229. /// <returns>卡类型名称</returns>
  230. private static string GetCardTypeName(int cardType)
  231. {
  232. switch (cardType)
  233. {
  234. case 1: return "接触式操作卡";
  235. case 2: return "非接触式操作卡";
  236. case 3: return "自动寻卡(接触式优先)";
  237. case 4: return "自动寻卡(非接触式优先)";
  238. default: return $"未知类型({cardType})";
  239. }
  240. }
  241. /// <summary>
  242. /// 根据错误码获取建议
  243. /// </summary>
  244. /// <param name="errorCode">错误码</param>
  245. /// <returns>错误建议</returns>
  246. private static string GetErrorSuggestion(long errorCode)
  247. {
  248. switch (errorCode)
  249. {
  250. case -11:
  251. return "请检查社保卡是否正确插入读卡器,确保卡片与读卡器接触良好";
  252. case -1:
  253. return "读卡器硬件可能未正确连接或驱动问题,请检查设备连接";
  254. case -2:
  255. return "卡片认证失败,请确认卡片为有效的社保卡";
  256. case -3:
  257. return "PSAM卡认证失败,请检查PSAM卡是否正确安装";
  258. default:
  259. return $"未知错误码 {errorCode},请联系技术支持";
  260. }
  261. }
  262. #endregion
  263. #region 配置和状态管理
  264. /// <summary>
  265. /// 刷脸认证数据结构 - 与泰和保持一致
  266. /// </summary>
  267. public class FaceAuthData
  268. {
  269. public string authNo { get; set; }
  270. }
  271. private static bool isInitialized = false;
  272. private static JiangSuConfig currentConfig = null;
  273. /// <summary>
  274. /// 江苏医保配置类 - 严格按照规范v0.9.9.15定义的13个必需参数(全部大写)
  275. /// 注意:这里写死了测试参数,生产环境请修改为实际值
  276. /// </summary>
  277. public class JiangSuConfig
  278. {
  279. /// <summary>
  280. /// 1. 服务端IP地址 - 写死测试值
  281. /// </summary>
  282. public string IP { get; set; } = "10.61.165.3";
  283. /// <summary>
  284. /// 2. 服务端端口 - 写死测试值
  285. /// </summary>
  286. public int PORT { get; set; } = 8086;
  287. /// <summary>
  288. /// 3. 超时时间(单位秒) - 写死测试值
  289. /// </summary>
  290. public int TIMEOUT { get; set; } = 30;
  291. /// <summary>
  292. /// 4. 动态库日志目录 - 写死测试值,确保目录存在
  293. /// </summary>
  294. public string LOG_PATH { get; set; } = "E:\\huaihaiProject\\readCard\\ThCardReader\\logs\\";
  295. /// <summary>
  296. /// 5. 电子凭证中台URL - 写死测试值
  297. /// </summary>
  298. // public string EC_URL { get; set; } = "http://10.61.165.3:8086/localcfc/api/hsecfc/localQrCodeQuery";
  299. public string EC_URL { get; set; } = "http://10.58.33.207:10086/localcfc/api/hsecfc/localQrCodeQuery";
  300. /// <summary>
  301. /// 6. 社保卡验证密码方式(1:验证卡pin,2:验证数据库密码) - 写死测试值
  302. /// 医院不需要输入密码,使用2:验证数据库密码
  303. /// </summary>
  304. public string CARD_PASSTYPE { get; set; } = "2";
  305. /// <summary>
  306. /// 7. CSB的_api_name - 写死测试值,请替换为实际申请的值
  307. /// </summary>
  308. public string API_NAME { get; set; } = "hssServives";
  309. /// <summary>
  310. /// 8. CSB的_api_version - 写死测试值
  311. /// </summary>
  312. public string API_VERSION { get; set; } = "1.0.0";
  313. /// <summary>
  314. /// 9. CSB的_api_access_key - 写死测试值,请替换为实际申请的值
  315. /// </summary>
  316. public string ACCESS_KEY { get; set; } = "090ea4c914324ee38b6978365df46a80";
  317. /// <summary>
  318. /// 10. CSB的secretKey - 写死测试值,请替换为实际申请的值
  319. /// </summary>
  320. public string SECRETKEY { get; set; } = "CMgTEMfftMP0EMMoliOs65wZmv8=";
  321. /// <summary>
  322. /// 11. 定点编号 - 写死测试值,请替换为医院实际编号
  323. /// </summary>
  324. public string ORG_ID { get; set; } = "H32132200561";
  325. /// <summary>
  326. /// 12. JSON对象字符串(预留) - 写死测试值
  327. /// </summary>
  328. public string EXT { get; set; } = "{}";
  329. /// <summary>
  330. /// 13. 定点所属行政区划代码 - 写死测试值(江苏宿迁沭阳)
  331. /// </summary>
  332. public string AREA_CODE { get; set; } = "321322";
  333. /// <summary>
  334. /// 江苏医保刷脸专用:写死的服务地址 - 与泰和完全一致的配置
  335. /// </summary>
  336. public string FACE_SERVICE_URL { get; set; } = "http://10.58.33.207:10086/localcfc/api/hsecfc/localQrCodeQuery";
  337. /// <summary>
  338. /// 江苏医保刷脸专用:写死的机构编号 - 与泰和完全一致的配置
  339. /// </summary>
  340. public string FACE_ORG_ID { get; set; } = "H32132200561";
  341. /// <summary>
  342. /// 转换为JSON字符串(严格按照规范大写参数名格式)
  343. /// </summary>
  344. public string ToJson()
  345. {
  346. var configObject = new
  347. {
  348. IP = this.IP,
  349. PORT = this.PORT,
  350. TIMEOUT = this.TIMEOUT,
  351. LOG_PATH = this.LOG_PATH,
  352. EC_URL = this.EC_URL,
  353. CARD_PASSTYPE = this.CARD_PASSTYPE,
  354. API_NAME = this.API_NAME,
  355. API_VERSION = this.API_VERSION,
  356. ACCESS_KEY = this.ACCESS_KEY,
  357. SECRETKEY = this.SECRETKEY,
  358. ORG_ID = this.ORG_ID,
  359. EXT = this.EXT,
  360. AREA_CODE = this.AREA_CODE
  361. };
  362. return JsonConvert.SerializeObject(configObject, Formatting.None);
  363. }
  364. }
  365. #endregion
  366. #region 核心业务方法
  367. /// <summary>
  368. /// 初始化江苏医保社保卡读取系统
  369. /// </summary>
  370. /// <param name="config">配置参数</param>
  371. /// <returns>初始化结果</returns>
  372. public static JObject Initialize(JiangSuConfig config = null)
  373. {
  374. var result = new JObject();
  375. try
  376. {
  377. // 先检查DLL文件并确保其在正确位置
  378. var dllCheckResult = EnsureDllExists();
  379. if (!(bool)dllCheckResult["success"])
  380. {
  381. // 如果DLL检查失败,直接返回错误
  382. return dllCheckResult;
  383. }
  384. // 使用默认配置或传入的配置
  385. if (config == null)
  386. {
  387. config = new JiangSuConfig();
  388. }
  389. // 确保日志目录存在
  390. if (!Directory.Exists(config.LOG_PATH))
  391. {
  392. Directory.CreateDirectory(config.LOG_PATH);
  393. }
  394. // 构造初始化JSON参数
  395. string initJson = config.ToJson();
  396. StringBuilder errMsg = new StringBuilder(2048);
  397. // 调用DLL初始化函数
  398. int initResult = Init(initJson, errMsg);
  399. if (initResult == 0)
  400. {
  401. isInitialized = true;
  402. currentConfig = config;
  403. result["success"] = true;
  404. result["code"] = 200;
  405. result["message"] = "江苏医保社保卡系统初始化成功";
  406. result["device"] = "江苏医保社保卡读卡器";
  407. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  408. result["dllErrorMsg"] = ""; // 成功时错误信息为空
  409. result["errMsgErrorMsg"] = errMsg.ToString(); // 输出参数pErrMsg初始化错误时候返回错误信息,成功时候返回空。
  410. result["initJsonMsg"] = initJson; // 初始化JSON参数
  411. result["initResultMsg"] = initResult; // 0表示成功;非0表示失败。
  412. }
  413. else
  414. {
  415. string errorMsg = errMsg.ToString();
  416. result["success"] = false;
  417. result["code"] = 1000 + initResult;
  418. result["message"] = $"江苏医保系统初始化失败: {errorMsg}";
  419. result["device"] = "江苏医保社保卡读卡器";
  420. result["errorCode"] = initResult;
  421. result["dllErrorMsg"] = errorMsg; // 原始DLL错误信息
  422. result["errMsgErrorMsg"] = errMsg.ToString(); // 输出参数pErrMsg初始化错误时候返回错误信息,成功时候返回空。
  423. result["initJsonMsg"] = initJson; // 初始化JSON参数
  424. result["initResultMsg"] = initResult; // 0表示成功;非0表示失败。
  425. }
  426. }
  427. catch (Exception ex)
  428. {
  429. result["success"] = false;
  430. result["code"] = 1001;
  431. result["message"] = $"江苏医保系统初始化异常: {ex.Message}";
  432. result["device"] = "江苏医保社保卡读卡器";
  433. result["exception"] = ex.GetType().Name;
  434. result["dllErrorMsg"] = $"系统异常: {ex.Message}"; // 异常时的错误信息
  435. }
  436. return result;
  437. }
  438. /// <summary>
  439. /// 读取社保卡基本信息
  440. /// 返回标准38号文格式数据
  441. /// </summary>
  442. /// <param name="autoVerifyPIN">是否自动验证PIN码</param>
  443. /// <param name="autoInit">是否自动初始化(默认false保持向后兼容)</param>
  444. /// <returns>读卡结果</returns>
  445. public static JObject ReadSocialCard(bool autoVerifyPIN = false, bool autoInit = false)
  446. {
  447. var result = new JObject();
  448. try
  449. {
  450. // 检查初始化状态
  451. if (!isInitialized)
  452. {
  453. if (autoInit)
  454. {
  455. // 智能初始化模式:自动使用默认配置初始化
  456. var autoInitResult = Initialize();
  457. if (!(bool)autoInitResult["success"])
  458. {
  459. result["success"] = false;
  460. result["code"] = 1002;
  461. result["message"] = "江苏医保系统自动初始化失败,请检查设备连接";
  462. result["device"] = "江苏医保社保卡读卡器";
  463. result["autoInitError"] = autoInitResult["message"];
  464. result["interface"] = "江苏医保接口 v0.9.9.15";
  465. result["interfaceType"] = "medical";
  466. result["initMode"] = "auto";
  467. return result;
  468. }
  469. else
  470. {
  471. // 自动初始化成功,记录日志
  472. result["autoInitSuccess"] = true;
  473. result["initMode"] = "auto";
  474. }
  475. }
  476. else
  477. {
  478. // 严格模式:保持原有行为,要求用户先初始化
  479. result["success"] = false;
  480. result["code"] = 1002;
  481. result["message"] = "江苏医保系统未初始化,请先调用初始化接口";
  482. result["device"] = "江苏医保社保卡读卡器";
  483. result["interface"] = "江苏医保接口 v0.9.9.15";
  484. result["interfaceType"] = "medical";
  485. result["initMode"] = "strict";
  486. result["suggestion"] = "请先调用 Initialize() 方法或设置 autoInit=true 启用自动初始化";
  487. return result;
  488. }
  489. }
  490. // 如果需要自动验证PIN码
  491. if (autoVerifyPIN)
  492. {
  493. var pinResult = VerifyCardPIN();
  494. if (!(bool)pinResult["success"])
  495. {
  496. return pinResult; // 返回PIN验证失败结果
  497. }
  498. }
  499. // 读取社保卡信息 - 按规范要求分配内存
  500. StringBuilder cardInfo = new StringBuilder(2048); // 基本信息缓冲区
  501. StringBuilder busiCardInfo = new StringBuilder(8192); // 业务信息缓冲区
  502. int readResult = ReadCardBas(cardInfo, busiCardInfo);
  503. if (readResult == 0)
  504. {
  505. string rawCardData = cardInfo.ToString();
  506. string rawBusiData = busiCardInfo.ToString();
  507. // 解析38号文格式数据
  508. var parsedData = Parse38DocumentFormat(rawCardData, rawBusiData);
  509. result["success"] = true;
  510. result["code"] = 200;
  511. result["message"] = "社保卡读取成功";
  512. result["device"] = "江苏医保社保卡读卡器";
  513. result["data"] = parsedData;
  514. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  515. result["interface"] = "江苏医保接口 v0.9.9.15";
  516. result["interfaceType"] = "medical"; // 标记为医保接口
  517. result["initMode"] = autoInit ? "auto" : "manual"; // 记录初始化模式
  518. // 添加原始读卡数据
  519. result["rawCardData"] = rawCardData;
  520. result["rawBusiData"] = rawBusiData;
  521. }
  522. else
  523. {
  524. result["success"] = false;
  525. result["code"] = 2000 + readResult;
  526. result["message"] = $"社保卡读取失败,错误码: {readResult}";
  527. result["device"] = "江苏医保社保卡读卡器";
  528. result["errorCode"] = readResult;
  529. result["interface"] = "江苏医保接口 v0.9.9.15";
  530. result["interfaceType"] = "medical"; // 标记为医保接口
  531. result["initMode"] = autoInit ? "auto" : "manual"; // 记录初始化模式
  532. // 读卡失败时也记录原始数据(可能为空)
  533. result["rawCardData"] = cardInfo.ToString();
  534. result["rawBusiData"] = busiCardInfo.ToString();
  535. }
  536. }
  537. catch (Exception ex)
  538. {
  539. result["success"] = false;
  540. result["code"] = 2001;
  541. result["message"] = $"社保卡读取异常: {ex.Message}";
  542. result["device"] = "江苏医保社保卡读卡器";
  543. result["exception"] = ex.GetType().Name;
  544. result["interface"] = "江苏医保接口 v0.9.9.15";
  545. result["interfaceType"] = "medical"; // 标记为医保接口
  546. result["initMode"] = autoInit ? "auto" : "manual"; // 记录初始化模式
  547. }
  548. return result;
  549. }
  550. /// <summary>
  551. /// 验证社保卡PIN码
  552. /// </summary>
  553. /// <returns>验证结果</returns>
  554. public static JObject VerifyCardPIN()
  555. {
  556. var result = new JObject();
  557. try
  558. {
  559. if (!isInitialized)
  560. {
  561. result["success"] = false;
  562. result["code"] = 1002;
  563. result["message"] = "江苏医保系统未初始化";
  564. return result;
  565. }
  566. StringBuilder outBuff = new StringBuilder(1024);
  567. int verifyResult = VerifyPIN(outBuff);
  568. if (verifyResult == 0)
  569. {
  570. result["success"] = true;
  571. result["code"] = 200;
  572. result["message"] = "PIN码验证成功";
  573. result["device"] = "江苏医保社保卡读卡器";
  574. result["data"] = outBuff.ToString();
  575. }
  576. else
  577. {
  578. result["success"] = false;
  579. result["code"] = 3000 + verifyResult;
  580. result["message"] = $"PIN码验证失败,错误码: {verifyResult}";
  581. result["errorCode"] = verifyResult;
  582. }
  583. }
  584. catch (Exception ex)
  585. {
  586. result["success"] = false;
  587. result["code"] = 3001;
  588. result["message"] = $"PIN码验证异常: {ex.Message}";
  589. result["exception"] = ex.GetType().Name;
  590. }
  591. return result;
  592. }
  593. /// <summary>
  594. /// 修改社保卡PIN码
  595. /// </summary>
  596. /// <returns>修改结果</returns>
  597. public static JObject ChangeCardPIN()
  598. {
  599. var result = new JObject();
  600. try
  601. {
  602. if (!isInitialized)
  603. {
  604. result["success"] = false;
  605. result["code"] = 1002;
  606. result["message"] = "江苏医保系统未初始化";
  607. return result;
  608. }
  609. StringBuilder outBuff = new StringBuilder(1024);
  610. int changeResult = ChangePIN(outBuff);
  611. if (changeResult == 0)
  612. {
  613. result["success"] = true;
  614. result["code"] = 200;
  615. result["message"] = "PIN码修改成功";
  616. result["device"] = "江苏医保社保卡读卡器";
  617. result["data"] = outBuff.ToString();
  618. }
  619. else
  620. {
  621. result["success"] = false;
  622. result["code"] = 4000 + changeResult;
  623. result["message"] = $"PIN码修改失败,错误码: {changeResult}";
  624. result["errorCode"] = changeResult;
  625. }
  626. }
  627. catch (Exception ex)
  628. {
  629. result["success"] = false;
  630. result["code"] = 4001;
  631. result["message"] = $"PIN码修改异常: {ex.Message}";
  632. result["exception"] = ex.GetType().Name;
  633. }
  634. return result;
  635. }
  636. /// <summary>
  637. /// 四合一介质获取个人信息
  638. /// 支持社保卡、电子凭证、电子社保卡、身份证
  639. /// </summary>
  640. /// <param name="mediaType">介质类型</param>
  641. /// <param name="inputData">输入数据</param>
  642. /// <returns>个人信息</returns>
  643. public static JObject GetPersonInfo(string mediaType = "socialcard", string inputData = "")
  644. {
  645. var result = new JObject();
  646. try
  647. {
  648. if (!isInitialized)
  649. {
  650. result["success"] = false;
  651. result["code"] = 1002;
  652. result["message"] = "江苏医保系统未初始化";
  653. return result;
  654. }
  655. // 构造输入参数JSON
  656. var inputObject = new
  657. {
  658. mediaType = mediaType,
  659. inputData = inputData
  660. };
  661. string inputJson = JsonConvert.SerializeObject(inputObject);
  662. StringBuilder outData = new StringBuilder(8192);
  663. int getResult = GetPersonInfo(inputJson, outData);
  664. if (getResult == 0)
  665. {
  666. string responseData = outData.ToString();
  667. var parsedInfo = ParsePersonInfo(responseData, mediaType);
  668. result["success"] = true;
  669. result["code"] = 200;
  670. result["message"] = "个人信息获取成功";
  671. result["device"] = "江苏医保社保卡读卡器";
  672. result["mediaType"] = mediaType;
  673. result["data"] = parsedInfo;
  674. }
  675. else
  676. {
  677. result["success"] = false;
  678. result["code"] = 5000 + getResult;
  679. result["message"] = $"个人信息获取失败,错误码: {getResult}";
  680. result["errorCode"] = getResult;
  681. }
  682. }
  683. catch (Exception ex)
  684. {
  685. result["success"] = false;
  686. result["code"] = 5001;
  687. result["message"] = $"个人信息获取异常: {ex.Message}";
  688. result["exception"] = ex.GetType().Name;
  689. }
  690. return result;
  691. }
  692. /// <summary>
  693. /// 获取设备状态
  694. /// </summary>
  695. /// <returns>设备状态信息</returns>
  696. public static JObject GetDeviceStatus()
  697. {
  698. var result = new JObject();
  699. try
  700. {
  701. result["success"] = true;
  702. result["code"] = 200;
  703. result["message"] = "设备状态获取成功";
  704. result["device"] = "江苏医保社保卡读卡器";
  705. result["data"] = new JObject
  706. {
  707. ["initialized"] = isInitialized,
  708. ["configLoaded"] = currentConfig != null,
  709. ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")
  710. };
  711. }
  712. catch (Exception ex)
  713. {
  714. result["success"] = false;
  715. result["code"] = 7001;
  716. result["message"] = $"设备状态获取异常: {ex.Message}";
  717. }
  718. return result;
  719. }
  720. /// <summary>
  721. /// 重置系统内部状态
  722. /// </summary>
  723. /// <returns>重置结果</returns>
  724. public static JObject ResetSystemState()
  725. {
  726. var result = new JObject();
  727. try
  728. {
  729. // 重置内部状态(不调用DLL函数)
  730. isInitialized = false;
  731. currentConfig = null;
  732. result["success"] = true;
  733. result["code"] = 200;
  734. result["message"] = "系统状态重置成功";
  735. result["device"] = "江苏医保社保卡读卡器";
  736. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  737. }
  738. catch (Exception ex)
  739. {
  740. result["success"] = false;
  741. result["code"] = 8001;
  742. result["message"] = $"系统状态重置异常: {ex.Message}";
  743. }
  744. return result;
  745. }
  746. /// <summary>
  747. /// 确保DLL文件在正确位置
  748. /// </summary>
  749. /// <returns>操作结果</returns>
  750. private static JObject EnsureDllExists()
  751. {
  752. var result = new JObject();
  753. try
  754. {
  755. string programDir = AppDomain.CurrentDomain.BaseDirectory;
  756. string targetDllPath = Path.Combine(programDir, "HeaSecReadInfo.dll");
  757. // 如果目标位置已经有DLL文件,直接返回成功
  758. if (File.Exists(targetDllPath))
  759. {
  760. result["success"] = true;
  761. result["code"] = 200;
  762. result["message"] = "HeaSecReadInfo.dll文件已就绪";
  763. result["device"] = "江苏医保社保卡读卡器";
  764. return result;
  765. }
  766. // 尝试从可能的位置复制DLL文件
  767. string[] possiblePaths = {
  768. // 当前目录及其子目录
  769. Path.Combine(programDir, "HeaSecReadInfo.dll"),
  770. Path.Combine(programDir, "bin", "Debug", "HeaSecReadInfo.dll"),
  771. Path.Combine(programDir, "bin", "Release", "HeaSecReadInfo.dll"),
  772. // 上级目录搜索
  773. Path.Combine(programDir, "..", "HeaSecReadInfo.dll"),
  774. Path.Combine(programDir, "..", "bin", "Debug", "HeaSecReadInfo.dll"),
  775. Path.Combine(programDir, "..", "bin", "Release", "HeaSecReadInfo.dll"),
  776. Path.Combine(programDir, "..", "..", "bin", "Debug", "HeaSecReadInfo.dll"),
  777. Path.Combine(programDir, "..", "..", "bin", "Release", "HeaSecReadInfo.dll"),
  778. // ThCardReader项目目录
  779. Path.Combine(programDir, "..", "..", "ThCardReader", "bin", "Debug", "HeaSecReadInfo.dll"),
  780. Path.Combine(programDir, "..", "..", "ThCardReader", "bin", "Release", "HeaSecReadInfo.dll"),
  781. // 当前工作目录
  782. Path.Combine(Directory.GetCurrentDirectory(), "HeaSecReadInfo.dll"),
  783. Path.Combine(Directory.GetCurrentDirectory(), "bin", "Debug", "HeaSecReadInfo.dll"),
  784. Path.Combine(Directory.GetCurrentDirectory(), "bin", "Release", "HeaSecReadInfo.dll"),
  785. // 绝对路径搜索(根据错误信息中的路径)
  786. @"E:\huaihaiProject\readCard\ThCardReader\ThCardReader\bin\Debug\HeaSecReadInfo.dll",
  787. @"E:\huaihaiProject\readCard\ThCardReader\bin\Debug\HeaSecReadInfo.dll"
  788. };
  789. string sourcePath = null;
  790. foreach (string path in possiblePaths)
  791. {
  792. if (File.Exists(path))
  793. {
  794. sourcePath = path;
  795. break;
  796. }
  797. }
  798. if (sourcePath != null)
  799. {
  800. // 复制DLL文件到程序目录
  801. File.Copy(sourcePath, targetDllPath, true);
  802. result["success"] = true;
  803. result["code"] = 200;
  804. result["message"] = "HeaSecReadInfo.dll文件已复制到位";
  805. result["device"] = "江苏医保社保卡读卡器";
  806. result["data"] = new JObject
  807. {
  808. ["sourcePath"] = sourcePath,
  809. ["targetPath"] = targetDllPath
  810. };
  811. }
  812. else
  813. {
  814. result["success"] = false;
  815. result["code"] = 8001;
  816. result["message"] = "找不到HeaSecReadInfo.dll文件";
  817. result["device"] = "江苏医保社保卡读卡器";
  818. result["data"] = new JObject
  819. {
  820. ["searchedPaths"] = string.Join("; ", possiblePaths),
  821. ["programDirectory"] = programDir,
  822. ["suggestion"] = "请确保HeaSecReadInfo.dll文件在程序目录或bin/Debug目录中"
  823. };
  824. }
  825. }
  826. catch (Exception ex)
  827. {
  828. result["success"] = false;
  829. result["code"] = 8002;
  830. result["message"] = $"DLL文件检查异常: {ex.Message}";
  831. result["device"] = "江苏医保社保卡读卡器";
  832. result["exception"] = ex.GetType().Name;
  833. }
  834. return result;
  835. }
  836. /// <summary>
  837. /// 检查DLL文件是否存在并可访问
  838. /// </summary>
  839. /// <returns>检查结果</returns>
  840. public static JObject CheckDllExists()
  841. {
  842. var result = new JObject();
  843. try
  844. {
  845. // 检查程序目录下的DLL文件
  846. string programDir = AppDomain.CurrentDomain.BaseDirectory;
  847. string dllPath = Path.Combine(programDir, "HeaSecReadInfo.dll");
  848. if (File.Exists(dllPath))
  849. {
  850. FileInfo fileInfo = new FileInfo(dllPath);
  851. result["success"] = true;
  852. result["code"] = 200;
  853. result["message"] = "HeaSecReadInfo.dll文件检查通过";
  854. result["device"] = "江苏医保社保卡读卡器";
  855. result["data"] = new JObject
  856. {
  857. ["dllPath"] = dllPath,
  858. ["fileSize"] = fileInfo.Length,
  859. ["lastModified"] = fileInfo.LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss"),
  860. ["programDirectory"] = programDir
  861. };
  862. }
  863. else
  864. {
  865. result["success"] = false;
  866. result["code"] = 8001;
  867. result["message"] = "HeaSecReadInfo.dll文件不存在";
  868. result["device"] = "江苏医保社保卡读卡器";
  869. result["data"] = new JObject
  870. {
  871. ["expectedPath"] = dllPath,
  872. ["programDirectory"] = programDir,
  873. ["suggestion"] = "请将HeaSecReadInfo.dll文件复制到程序目录"
  874. };
  875. }
  876. }
  877. catch (Exception ex)
  878. {
  879. result["success"] = false;
  880. result["code"] = 8002;
  881. result["message"] = $"DLL文件检查异常: {ex.Message}";
  882. result["device"] = "江苏医保社保卡读卡器";
  883. result["exception"] = ex.GetType().Name;
  884. }
  885. return result;
  886. }
  887. /// <summary>
  888. /// 诊断读卡器硬件和社保卡状态
  889. /// </summary>
  890. /// <returns>诊断结果</returns>
  891. public static JObject DiagnoseCardReader()
  892. {
  893. var result = new JObject();
  894. try
  895. {
  896. result["device"] = "江苏医保社保卡读卡器";
  897. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  898. var diagnostics = new JObject();
  899. // 1. 检查DLL状态
  900. var dllCheck = CheckDllExists();
  901. diagnostics["dllStatus"] = dllCheck;
  902. // 2. 检查初始化状态
  903. diagnostics["initStatus"] = new JObject
  904. {
  905. ["isInitialized"] = isInitialized,
  906. ["hasConfig"] = currentConfig != null,
  907. ["configDetails"] = currentConfig?.ToJson() ?? "未配置"
  908. };
  909. // 3. 尝试不同的读卡配置
  910. var cardTests = new JObject();
  911. // 测试无PIN模式
  912. try
  913. {
  914. var config = new JiangSuConfig();
  915. config.CARD_PASSTYPE = "2"; // 验证数据库密码(跳过PIN)
  916. var initResult = Initialize(config);
  917. var readResult = ReadSocialCard(false); // 不验证PIN
  918. cardTests["noPinTest"] = new JObject
  919. {
  920. ["config"] = "CARD_PASSTYPE=2(跳过PIN验证)",
  921. ["initResult"] = initResult,
  922. ["readResult"] = readResult
  923. };
  924. }
  925. catch (Exception ex)
  926. {
  927. cardTests["noPinTest"] = new JObject
  928. {
  929. ["config"] = "CARD_PASSTYPE=2(跳过PIN验证)",
  930. ["error"] = ex.Message
  931. };
  932. }
  933. // 测试PIN模式
  934. try
  935. {
  936. var config = new JiangSuConfig();
  937. config.CARD_PASSTYPE = "1"; // 验证卡PIN
  938. var initResult = Initialize(config);
  939. var readResult = ReadSocialCard(false); // 不自动验证PIN
  940. cardTests["pinTest"] = new JObject
  941. {
  942. ["config"] = "CARD_PASSTYPE=1(需要PIN验证)",
  943. ["initResult"] = initResult,
  944. ["readResult"] = readResult
  945. };
  946. }
  947. catch (Exception ex)
  948. {
  949. cardTests["pinTest"] = new JObject
  950. {
  951. ["config"] = "CARD_PASSTYPE=1(需要PIN验证)",
  952. ["error"] = ex.Message
  953. };
  954. }
  955. diagnostics["cardTests"] = cardTests;
  956. // 4. 分析结果
  957. var analysis = new JObject();
  958. analysis["recommendations"] = new JArray();
  959. // 检查是否有成功的读卡
  960. bool hasSuccessfulRead = false;
  961. if (cardTests["noPinTest"] != null && cardTests["noPinTest"]["readResult"] != null)
  962. {
  963. var noPinResult = (JObject)cardTests["noPinTest"]["readResult"];
  964. if (noPinResult["success"] != null && (bool)noPinResult["success"])
  965. {
  966. hasSuccessfulRead = true;
  967. ((JArray)analysis["recommendations"]).Add("✅ 使用CARD_PASSTYPE=2可以成功读卡");
  968. }
  969. else if (noPinResult["errorCode"] != null)
  970. {
  971. int errorCode = (int)noPinResult["errorCode"];
  972. if (errorCode == -11)
  973. {
  974. ((JArray)analysis["recommendations"]).Add("⚠️ 错误码-11:请检查社保卡是否正确插入并接触良好");
  975. }
  976. }
  977. }
  978. if (cardTests["pinTest"] != null && cardTests["pinTest"]["readResult"] != null)
  979. {
  980. var pinResult = (JObject)cardTests["pinTest"]["readResult"];
  981. if (pinResult["success"] != null && (bool)pinResult["success"])
  982. {
  983. hasSuccessfulRead = true;
  984. ((JArray)analysis["recommendations"]).Add("✅ 使用CARD_PASSTYPE=1可以成功读卡");
  985. }
  986. }
  987. if (!hasSuccessfulRead)
  988. {
  989. ((JArray)analysis["recommendations"]).Add("❌ 两种模式都无法读卡,可能的原因:");
  990. ((JArray)analysis["recommendations"]).Add(" 1. 社保卡未正确插入或损坏");
  991. ((JArray)analysis["recommendations"]).Add(" 2. 读卡器硬件故障或驱动问题");
  992. ((JArray)analysis["recommendations"]).Add(" 3. 网络配置参数错误");
  993. ((JArray)analysis["recommendations"]).Add(" 4. DLL文件版本不匹配");
  994. }
  995. analysis["overallStatus"] = hasSuccessfulRead ? "正常" : "异常";
  996. diagnostics["analysis"] = analysis;
  997. result["success"] = true;
  998. result["code"] = 200;
  999. result["message"] = "读卡器诊断完成";
  1000. result["data"] = diagnostics;
  1001. }
  1002. catch (Exception ex)
  1003. {
  1004. result["success"] = false;
  1005. result["code"] = 9001;
  1006. result["message"] = $"读卡器诊断异常: {ex.Message}";
  1007. result["device"] = "江苏医保社保卡读卡器";
  1008. result["exception"] = ex.GetType().Name;
  1009. }
  1010. return result;
  1011. }
  1012. /// <summary>
  1013. /// 离线模式读取社保卡(跳过网络验证,仅测试本地读卡功能)
  1014. /// </summary>
  1015. /// <returns>读卡结果</returns>
  1016. public static JObject ReadSocialCardOffline()
  1017. {
  1018. var result = new JObject();
  1019. try
  1020. {
  1021. // 使用最简配置进行本地测试
  1022. var offlineConfig = new JiangSuConfig();
  1023. offlineConfig.IP = "127.0.0.1"; // 本地IP
  1024. offlineConfig.PORT = 8080; // 本地端口
  1025. offlineConfig.EC_URL = ""; // 空URL跳过网络验证
  1026. offlineConfig.ACCESS_KEY = ""; // 空密钥
  1027. offlineConfig.SECRETKEY = ""; // 空密钥
  1028. // 构造最简初始化参数
  1029. var simpleConfig = new
  1030. {
  1031. IP = "127.0.0.1",
  1032. PORT = 8080,
  1033. TIMEOUT = 5,
  1034. LOG_PATH = offlineConfig.LOG_PATH,
  1035. CARD_PASSTYPE = "2", // 跳过PIN验证
  1036. EC_URL = "",
  1037. API_NAME = "",
  1038. API_VERSION = "",
  1039. ACCESS_KEY = "",
  1040. SECRETKEY = "",
  1041. ORG_ID = "",
  1042. EXT = "{}",
  1043. AREA_CODE = ""
  1044. };
  1045. string initJson = JsonConvert.SerializeObject(simpleConfig, Formatting.None);
  1046. StringBuilder errMsg = new StringBuilder(2048);
  1047. // 尝试离线初始化
  1048. int initResult = Init(initJson, errMsg);
  1049. if (initResult == 0)
  1050. {
  1051. // 初始化成功,尝试读卡
  1052. isInitialized = true;
  1053. currentConfig = offlineConfig;
  1054. StringBuilder cardInfo = new StringBuilder(2048);
  1055. StringBuilder busiCardInfo = new StringBuilder(8192);
  1056. int readResult = ReadCardBas(cardInfo, busiCardInfo);
  1057. if (readResult == 0)
  1058. {
  1059. string rawCardData = cardInfo.ToString();
  1060. string rawBusiData = busiCardInfo.ToString();
  1061. var parsedData = Parse38DocumentFormat(rawCardData, rawBusiData);
  1062. result["success"] = true;
  1063. result["code"] = 200;
  1064. result["message"] = "离线模式读卡成功";
  1065. result["device"] = "江苏医保社保卡读卡器(离线模式)";
  1066. result["data"] = parsedData;
  1067. result["mode"] = "offline";
  1068. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  1069. result["dllErrorMsg"] = ""; // 成功时错误信息为空
  1070. // 添加原始读卡数据
  1071. result["rawCardData"] = rawCardData;
  1072. result["rawBusiData"] = rawBusiData;
  1073. }
  1074. else
  1075. {
  1076. result["success"] = false;
  1077. result["code"] = 2000 + readResult;
  1078. result["message"] = $"离线模式读卡失败,错误码: {readResult}";
  1079. result["device"] = "江苏医保社保卡读卡器(离线模式)";
  1080. result["mode"] = "offline";
  1081. result["errorCode"] = readResult;
  1082. result["dllErrorMsg"] = ""; // 读卡失败,但初始化成功
  1083. // 读卡失败时也记录原始数据(可能为空)
  1084. result["rawCardData"] = cardInfo.ToString();
  1085. result["rawBusiData"] = busiCardInfo.ToString();
  1086. if (readResult == -11)
  1087. {
  1088. result["suggestion"] = "请检查社保卡是否正确插入读卡器";
  1089. }
  1090. else if (readResult == -1)
  1091. {
  1092. result["suggestion"] = "读卡器硬件可能未正确连接或驱动问题";
  1093. }
  1094. }
  1095. }
  1096. else
  1097. {
  1098. string errorMsg = errMsg.ToString();
  1099. result["success"] = false;
  1100. result["code"] = 1000 + initResult;
  1101. result["message"] = $"离线模式初始化失败: {errorMsg}";
  1102. result["device"] = "江苏医保社保卡读卡器(离线模式)";
  1103. result["mode"] = "offline";
  1104. result["errorCode"] = initResult;
  1105. result["dllErrorMsg"] = errorMsg; // 原始DLL错误信息
  1106. }
  1107. }
  1108. catch (Exception ex)
  1109. {
  1110. result["success"] = false;
  1111. result["code"] = 9999;
  1112. result["message"] = $"离线模式异常: {ex.Message}";
  1113. result["device"] = "江苏医保社保卡读卡器(离线模式)";
  1114. result["mode"] = "offline";
  1115. result["exception"] = ex.GetType().Name;
  1116. result["dllErrorMsg"] = $"离线模式异常: {ex.Message}"; // 异常时的错误信息
  1117. }
  1118. return result;
  1119. }
  1120. /// <summary>
  1121. /// 测试不同网络配置对初始化的影响
  1122. /// </summary>
  1123. /// <returns>测试结果</returns>
  1124. public static JObject TestNetworkConfigurations()
  1125. {
  1126. var result = new JObject();
  1127. var tests = new JObject();
  1128. try
  1129. {
  1130. // 测试1:使用原始配置(10.61.165.3:8086)
  1131. var config1 = new JiangSuConfig();
  1132. config1.IP = "10.61.165.3";
  1133. config1.PORT = 8086;
  1134. config1.EC_URL = "http://10.61.165.3:8086/localcfc/api/hsecfc/localQrCodeQuery";
  1135. var test1 = TestSingleConfig(config1, "原始配置(10.61.165.3:8086)");
  1136. tests["originalConfig"] = test1;
  1137. // 测试2:使用HSM配置(10.79.157.29:8089)
  1138. var config2 = new JiangSuConfig();
  1139. config2.IP = "10.79.157.29";
  1140. config2.PORT = 8089;
  1141. config2.EC_URL = "http://10.79.157.29:8089/encryptionMachine/encryptionMachineBusiness";
  1142. var test2 = TestSingleConfig(config2, "HSM配置(10.79.157.29:8089)");
  1143. tests["hsmConfig"] = test2;
  1144. // 测试3:使用无效配置
  1145. var config3 = new JiangSuConfig();
  1146. config3.IP = "192.168.1.1";
  1147. config3.PORT = 9999;
  1148. config3.EC_URL = "http://192.168.1.1:9999/invalid";
  1149. var test3 = TestSingleConfig(config3, "无效配置(192.168.1.1:9999)");
  1150. tests["invalidConfig"] = test3;
  1151. // 测试4:空配置
  1152. var config4 = new JiangSuConfig();
  1153. config4.IP = "";
  1154. config4.PORT = 0;
  1155. config4.EC_URL = "";
  1156. config4.ACCESS_KEY = "";
  1157. config4.SECRETKEY = "";
  1158. var test4 = TestSingleConfig(config4, "空配置");
  1159. tests["emptyConfig"] = test4;
  1160. result["success"] = true;
  1161. result["code"] = 200;
  1162. result["message"] = "网络配置测试完成";
  1163. result["tests"] = tests;
  1164. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  1165. // 分析结果
  1166. var analysis = new JArray();
  1167. foreach (var test in tests)
  1168. {
  1169. var testResult = (JObject)test.Value;
  1170. var errorCode = testResult["errorCode"]?.ToString() ?? "无";
  1171. var config = testResult["configName"]?.ToString() ?? "未知";
  1172. analysis.Add($"{config}: 错误码 {errorCode}");
  1173. }
  1174. result["analysis"] = analysis;
  1175. }
  1176. catch (Exception ex)
  1177. {
  1178. result["success"] = false;
  1179. result["code"] = 9001;
  1180. result["message"] = $"网络配置测试异常: {ex.Message}";
  1181. result["exception"] = ex.GetType().Name;
  1182. }
  1183. return result;
  1184. }
  1185. /// <summary>
  1186. /// 测试单个配置
  1187. /// </summary>
  1188. private static JObject TestSingleConfig(JiangSuConfig config, string configName)
  1189. {
  1190. var testResult = new JObject();
  1191. testResult["configName"] = configName;
  1192. testResult["config"] = JObject.Parse(config.ToJson());
  1193. try
  1194. {
  1195. string initJson = config.ToJson();
  1196. StringBuilder errMsg = new StringBuilder(2048);
  1197. int initResult = Init(initJson, errMsg);
  1198. testResult["initResult"] = initResult;
  1199. testResult["errorMessage"] = errMsg.ToString();
  1200. testResult["errorCode"] = initResult;
  1201. testResult["dllErrorMsg"] = errMsg.ToString(); // 原始DLL错误信息
  1202. if (initResult == 0)
  1203. {
  1204. testResult["status"] = "初始化成功";
  1205. // 如果初始化成功,尝试读卡
  1206. StringBuilder cardInfo = new StringBuilder(2048);
  1207. StringBuilder busiCardInfo = new StringBuilder(8192);
  1208. int readResult = ReadCardBas(cardInfo, busiCardInfo);
  1209. testResult["readResult"] = readResult;
  1210. testResult["readStatus"] = readResult == 0 ? "读卡成功" : $"读卡失败,错误码: {readResult}";
  1211. }
  1212. else
  1213. {
  1214. testResult["status"] = $"初始化失败,错误码: {initResult}";
  1215. }
  1216. }
  1217. catch (Exception ex)
  1218. {
  1219. testResult["status"] = "测试异常";
  1220. testResult["error"] = ex.Message;
  1221. testResult["errorCode"] = -999;
  1222. testResult["dllErrorMsg"] = $"测试异常: {ex.Message}"; // 异常时的错误信息
  1223. }
  1224. return testResult;
  1225. }
  1226. #endregion
  1227. #region 电子凭证解码功能 (按照江苏医保接口规范v0.9.9.15实现)
  1228. /// <summary>
  1229. /// 江苏医保电子凭证解码
  1230. /// 严格按照《医疗保障信息平台定点基线版医药机构接口规范 v0.9.9.15》中的1.14.6电子凭证解码接口实现
  1231. /// </summary>
  1232. /// <param name="businessType">用码业务类型,如:01101-门诊挂号,01301-门诊结算,02121-药店购药</param>
  1233. /// <param name="operatorId">收款员编号</param>
  1234. /// <param name="operatorName">收款员姓名</param>
  1235. /// <param name="officeId">医保科室编号</param>
  1236. /// <param name="officeName">科室名称</param>
  1237. /// <returns>电子凭证解码结果</returns>
  1238. public static JObject ReadElectronicCertificate(string businessType = "01101", string operatorId = "system001",
  1239. string operatorName = "系统管理员", string officeId = "32760", string officeName = "医保科")
  1240. {
  1241. var result = new JObject();
  1242. try
  1243. {
  1244. // 检查初始化状态
  1245. if (!isInitialized)
  1246. {
  1247. // 自动初始化
  1248. var autoInitResult = Initialize();
  1249. if (!(bool)autoInitResult["success"])
  1250. {
  1251. result["success"] = false;
  1252. result["code"] = 1002;
  1253. result["message"] = "江苏医保系统未初始化,电子凭证解码失败";
  1254. result["device"] = "江苏医保电子凭证解码器";
  1255. result["autoInitError"] = autoInitResult["message"];
  1256. result["type"] = "qrcode";
  1257. return result;
  1258. }
  1259. }
  1260. // 获取当前配置
  1261. if (currentConfig == null)
  1262. {
  1263. result["success"] = false;
  1264. result["code"] = 1003;
  1265. result["message"] = "江苏医保配置信息为空";
  1266. result["device"] = "江苏医保电子凭证解码器";
  1267. result["type"] = "qrcode";
  1268. return result;
  1269. }
  1270. // 按照江苏医保接口规范1.14.6构造输入参数
  1271. var inputData = new
  1272. {
  1273. data = new
  1274. {
  1275. orgId = currentConfig.ORG_ID, // 定点编号
  1276. businessType = businessType, // 用码业务类型
  1277. operatorId = operatorId, // 收款员编号
  1278. operatorName = operatorName, // 收款员姓名
  1279. officeId = officeId, // 医保科室编号
  1280. officeName = officeName // 科室名称
  1281. },
  1282. transType = "ec.query", // 固定值:ec.query
  1283. orgId = currentConfig.ORG_ID // 定点编号
  1284. };
  1285. // JSON序列化输入参数
  1286. string jsonInput = JsonConvert.SerializeObject(inputData, Formatting.None);
  1287. // 按照规范分配8192字节输出缓冲区
  1288. StringBuilder outputBuffer = new StringBuilder(8192);
  1289. // 调用江苏医保DLL的EcQuery函数
  1290. int dllResult = EcQuery(jsonInput, outputBuffer);
  1291. if (dllResult == 0)
  1292. {
  1293. // 解析DLL返回的JSON数据
  1294. string responseJson = outputBuffer.ToString();
  1295. if (string.IsNullOrEmpty(responseJson))
  1296. {
  1297. result["success"] = false;
  1298. result["code"] = 1004;
  1299. result["message"] = "电子凭证解码返回数据为空";
  1300. result["device"] = "江苏医保电子凭证解码器";
  1301. result["type"] = "qrcode";
  1302. return result;
  1303. }
  1304. // 解析江苏医保返回的JSON数据
  1305. var jiangsuResponse = JObject.Parse(responseJson);
  1306. // 检查江苏医保接口返回的code字段
  1307. int responseCode = (int)(jiangsuResponse["code"] ?? -1);
  1308. if (responseCode == 0)
  1309. {
  1310. // 成功:提取患者信息
  1311. var patientData = jiangsuResponse["data"];
  1312. if (patientData != null)
  1313. {
  1314. result["success"] = true;
  1315. result["code"] = 200;
  1316. result["message"] = "江苏医保电子凭证解码成功";
  1317. result["device"] = "江苏医保电子凭证解码器";
  1318. result["type"] = "qrcode";
  1319. result["originalType"] = "jiangsu_ec";
  1320. result["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  1321. // 按照文档规范提取患者信息
  1322. var patientInfo = new
  1323. {
  1324. idNo = patientData["idNo"]?.ToString() ?? "", // 身份证号
  1325. userName = patientData["userName"]?.ToString() ?? "", // 姓名
  1326. idType = patientData["idType"]?.ToString() ?? "", // 证件类型
  1327. ecToken = patientData["ecToken"]?.ToString() ?? "", // 令牌
  1328. insuOrg = patientData["insuOrg"]?.ToString() ?? "", // 参保地区编码
  1329. ecIndexNo = patientData["ecIndexNo"]?.ToString() ?? "", // 电子凭证索引号
  1330. gender = patientData["gender"]?.ToString() ?? "", // 性别
  1331. birthday = patientData["birthday"]?.ToString() ?? "", // 出生日期
  1332. nationality = patientData["nationality"]?.ToString() ?? "", // 国籍
  1333. email = patientData["email"]?.ToString() ?? "", // 邮箱
  1334. extra = patientData["extra"]?.ToString() ?? "" // 扩展参数
  1335. };
  1336. // 按照泰和医院格式构造data字段(包含code/data/message结构)
  1337. var dllResponseFormat = new
  1338. {
  1339. code = 0,
  1340. data = patientInfo,
  1341. message = "交易成功"
  1342. };
  1343. result["data"] = JsonConvert.SerializeObject(dllResponseFormat);
  1344. // 保存原始江苏医保返回数据(用于调试和日志)
  1345. result["jiangsuOriginalData"] = responseJson;
  1346. result["businessType"] = businessType;
  1347. result["interfaceVersion"] = "v0.9.9.15";
  1348. // 添加兼容字段(与前端保持一致,采用泰和医院格式)
  1349. result["readCardResult"] = JsonConvert.SerializeObject(dllResponseFormat);
  1350. }
  1351. else
  1352. {
  1353. result["success"] = false;
  1354. result["code"] = 1005;
  1355. result["message"] = "电子凭证解码成功,但患者数据为空";
  1356. result["device"] = "江苏医保电子凭证解码器";
  1357. result["type"] = "qrcode";
  1358. result["jiangsuOriginalData"] = responseJson;
  1359. }
  1360. }
  1361. else
  1362. {
  1363. // 江苏医保接口返回失败
  1364. string errorMessage = jiangsuResponse["message"]?.ToString() ?? "电子凭证解码失败";
  1365. result["success"] = false;
  1366. result["code"] = 2000 + responseCode; // 使用2xxx系列表示江苏医保接口错误
  1367. result["message"] = $"江苏医保电子凭证解码失败: {errorMessage}";
  1368. result["device"] = "江苏医保电子凭证解码器";
  1369. result["type"] = "qrcode";
  1370. result["jiangsuErrorCode"] = responseCode;
  1371. result["jiangsuErrorMessage"] = errorMessage;
  1372. result["jiangsuOriginalData"] = responseJson;
  1373. result["businessType"] = businessType;
  1374. }
  1375. }
  1376. else
  1377. {
  1378. // DLL函数调用失败
  1379. string errorInfo = outputBuffer.ToString();
  1380. result["success"] = false;
  1381. result["code"] = 3000 + dllResult; // 使用3xxx系列表示DLL调用错误
  1382. result["message"] = $"江苏医保DLL调用失败,错误码: {dllResult}";
  1383. result["device"] = "江苏医保电子凭证解码器";
  1384. result["type"] = "qrcode";
  1385. result["dllErrorCode"] = dllResult;
  1386. result["dllErrorMessage"] = errorInfo;
  1387. result["businessType"] = businessType;
  1388. result["suggestion"] = GetEcQueryErrorSuggestion(dllResult);
  1389. }
  1390. }
  1391. catch (JsonException jsonEx)
  1392. {
  1393. result["success"] = false;
  1394. result["code"] = 1006;
  1395. result["message"] = $"电子凭证解码数据解析异常: {jsonEx.Message}";
  1396. result["device"] = "江苏医保电子凭证解码器";
  1397. result["type"] = "qrcode";
  1398. result["exception"] = jsonEx.GetType().Name;
  1399. result["businessType"] = businessType;
  1400. }
  1401. catch (Exception ex)
  1402. {
  1403. result["success"] = false;
  1404. result["code"] = 1007;
  1405. result["message"] = $"电子凭证解码系统异常: {ex.Message}";
  1406. result["device"] = "江苏医保电子凭证解码器";
  1407. result["type"] = "qrcode";
  1408. result["exception"] = ex.GetType().Name;
  1409. result["businessType"] = businessType;
  1410. }
  1411. return result;
  1412. }
  1413. /// <summary>
  1414. /// 根据业务场景获取科室名称
  1415. /// </summary>
  1416. /// <param name="businessType">业务类型代码</param>
  1417. /// <returns>科室名称</returns>
  1418. private static string GetDepartmentNameByBusinessType(string businessType)
  1419. {
  1420. switch (businessType)
  1421. {
  1422. case "01101": // 门诊挂号
  1423. return "挂号处";
  1424. case "01201": // 门诊问诊
  1425. case "01202": // 预约检查
  1426. case "01203": // 检查
  1427. case "01204": // 治疗
  1428. return "门诊科室";
  1429. case "01301": // 门诊结算
  1430. return "门诊收费处";
  1431. case "01302": // 取药
  1432. return "门诊药房";
  1433. case "01102": // 住院建档
  1434. case "01103": // 入院登记
  1435. case "01104": // 缴纳预缴金
  1436. return "住院处";
  1437. case "02121": // 药店购药
  1438. case "02122": // 下载外购处方
  1439. return "药店";
  1440. default:
  1441. return "医保科";
  1442. }
  1443. }
  1444. /// <summary>
  1445. /// 获取EcQuery错误码对应的建议
  1446. /// </summary>
  1447. /// <param name="errorCode">错误码</param>
  1448. /// <returns>错误建议</returns>
  1449. private static string GetEcQueryErrorSuggestion(int errorCode)
  1450. {
  1451. switch (errorCode)
  1452. {
  1453. case -1:
  1454. return "网络连接失败,请检查网络连接和医保平台服务状态";
  1455. case -2:
  1456. return "身份认证失败,请检查ACCESS_KEY和SECRET_KEY配置";
  1457. case -3:
  1458. return "参数格式错误,请检查业务类型和定点编号配置";
  1459. case -4:
  1460. return "电子凭证已过期,请提示患者刷新电子凭证";
  1461. case -5:
  1462. return "参保地不匹配,请确认患者参保地信息";
  1463. default:
  1464. return "电子凭证解码失败,请检查设备连接和配置信息";
  1465. }
  1466. }
  1467. /// <summary>
  1468. /// 验证业务类型代码是否有效
  1469. /// </summary>
  1470. /// <param name="businessType">业务类型代码</param>
  1471. /// <returns>是否有效</returns>
  1472. public static bool IsValidBusinessType(string businessType)
  1473. {
  1474. if (string.IsNullOrEmpty(businessType) || businessType.Length != 5)
  1475. return false;
  1476. // 按照江苏医保规范检查业务类型
  1477. string[] validTypes = {
  1478. // 医院业务类型
  1479. "01101", "01102", "01103", "01104", // 挂号、住院建档、入院登记、缴纳预缴金
  1480. "01201", "01202", "01203", "01204", // 问诊、预约检查、检查、治疗
  1481. "01301", "01302", "01303", "01304", "01305", // 结算、取药、取报告、打印票据和清单、病历材料复印
  1482. // 药店业务类型
  1483. "02121", "02122" // 药店购药、下载外购处方
  1484. };
  1485. return validTypes.Contains(businessType);
  1486. }
  1487. #endregion
  1488. #region 数据解析方法
  1489. /// <summary>
  1490. /// 解析38号文格式的社保卡数据
  1491. /// </summary>
  1492. /// <param name="cardData">卡基本信息</param>
  1493. /// <param name="busiData">业务信息</param>
  1494. /// <returns>解析后的数据</returns>
  1495. private static JObject Parse38DocumentFormat(string cardData, string busiData)
  1496. {
  1497. var result = new JObject();
  1498. try
  1499. {
  1500. // 按照38号文格式解析,使用管道符"|"分隔
  1501. string[] cardFields = cardData.Split('|');
  1502. if (cardFields.Length >= 11)
  1503. {
  1504. result["areaCode"] = cardFields[0]; // 发卡地区行政区划代码
  1505. result["socialSecurityNumber"] = cardFields[1]; // 社会保障号码
  1506. result["cardNumber"] = cardFields[2]; // 卡号
  1507. result["cardId"] = cardFields[3]; // 卡识别码
  1508. result["name"] = cardFields[4]; // 姓名
  1509. result["cardResetInfo"] = cardFields[5]; // 卡复位信息
  1510. result["specVersion"] = cardFields[6]; // 规范版本
  1511. result["issueDate"] = FormatDate(cardFields[7]); // 发卡日期
  1512. result["expireDate"] = FormatDate(cardFields[8]); // 卡有效期
  1513. result["terminalNumber"] = cardFields[9]; // 终端机编号
  1514. result["terminalDevice"] = cardFields[10]; // 终端设备号
  1515. }
  1516. // 解析业务信息(如果有)
  1517. if (!string.IsNullOrEmpty(busiData))
  1518. {
  1519. result["businessInfo"] = busiData;
  1520. }
  1521. result["format"] = "38号文标准格式";
  1522. result["parseTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  1523. }
  1524. catch (Exception ex)
  1525. {
  1526. result["error"] = $"数据解析失败: {ex.Message}";
  1527. }
  1528. return result;
  1529. }
  1530. /// <summary>
  1531. /// 解析四合一介质的个人信息
  1532. /// </summary>
  1533. /// <param name="responseData">响应数据</param>
  1534. /// <param name="mediaType">介质类型</param>
  1535. /// <returns>解析后的个人信息</returns>
  1536. private static JObject ParsePersonInfo(string responseData, string mediaType)
  1537. {
  1538. var result = new JObject();
  1539. try
  1540. {
  1541. // 尝试解析JSON格式的响应
  1542. if (responseData.StartsWith("{"))
  1543. {
  1544. var jsonData = JObject.Parse(responseData);
  1545. result = jsonData;
  1546. }
  1547. else
  1548. {
  1549. // 如果不是JSON,按不同介质类型解析
  1550. switch (mediaType.ToLower())
  1551. {
  1552. case "socialcard":
  1553. // 社保卡:按38号文格式解析
  1554. result = Parse38DocumentFormat(responseData, "");
  1555. break;
  1556. case "idcard":
  1557. // 身份证:按身份证格式解析
  1558. string[] idFields = responseData.Split('|');
  1559. if (idFields.Length >= 8)
  1560. {
  1561. result["idNumber"] = idFields[0];
  1562. result["name"] = idFields[1];
  1563. result["sex"] = idFields[2];
  1564. result["nation"] = idFields[3];
  1565. result["birthday"] = FormatDate(idFields[4]);
  1566. result["address"] = idFields[5];
  1567. result["issueDate"] = FormatDate(idFields[6]);
  1568. result["validDate"] = FormatDate(idFields[7]);
  1569. }
  1570. break;
  1571. case "electronic":
  1572. case "esocialcard":
  1573. // 电子凭证/电子社保卡:通常为JSON格式
  1574. result["rawData"] = responseData;
  1575. break;
  1576. default:
  1577. result["rawData"] = responseData;
  1578. break;
  1579. }
  1580. }
  1581. result["mediaType"] = mediaType;
  1582. result["parseTime"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
  1583. }
  1584. catch (Exception ex)
  1585. {
  1586. result["error"] = $"个人信息解析失败: {ex.Message}";
  1587. result["rawData"] = responseData;
  1588. }
  1589. return result;
  1590. }
  1591. /// <summary>
  1592. /// 格式化日期字符串
  1593. /// </summary>
  1594. /// <param name="dateStr">原始日期字符串</param>
  1595. /// <returns>格式化后的日期</returns>
  1596. private static string FormatDate(string dateStr)
  1597. {
  1598. if (string.IsNullOrEmpty(dateStr) || dateStr.Length < 8)
  1599. return dateStr;
  1600. // 尝试格式化YYYYMMDD为YYYY-MM-DD
  1601. if (dateStr.Length == 8 && dateStr.All(char.IsDigit))
  1602. {
  1603. return $"{dateStr.Substring(0, 4)}-{dateStr.Substring(4, 2)}-{dateStr.Substring(6, 2)}";
  1604. }
  1605. return dateStr;
  1606. }
  1607. #endregion
  1608. #region 江苏医保刷脸功能 (使用NationECCode.dll与泰和完全一致)
  1609. /// <summary>
  1610. /// 江苏医保刷脸功能 - 使用NationECCode.dll与泰和完全一致
  1611. /// 完全相同的实现:相同的DLL、相同的URL、相同的逻辑、相同的参数格式
  1612. /// 唯一区别:使用写死的配置参数
  1613. /// </summary>
  1614. /// <param name="biztype">业务类型</param>
  1615. /// <returns>刷脸结果</returns>
  1616. public static JObject ReadFace(string biztype)
  1617. {
  1618. JObject result = new JObject();
  1619. try
  1620. {
  1621. // 使用写死的江苏医保刷脸配置参数
  1622. if (null == currentConfig)
  1623. {
  1624. currentConfig = new JiangSuConfig();
  1625. }
  1626. // 第一步:获取授权码(cn.nhsa.ec.auth)
  1627. // 补全所有参数,即使为空也带上
  1628. var data = new {
  1629. businessType = biztype,
  1630. deviceType = "",
  1631. orgId = currentConfig.FACE_ORG_ID,
  1632. outBizNo = "",
  1633. operatorId = "",
  1634. operatorName = "",
  1635. officeId = "",
  1636. officeName = ""
  1637. };
  1638. var authRequest = new {
  1639. data = data,
  1640. orgId = currentConfig.FACE_ORG_ID,
  1641. transType = "cn.nhsa.ec.auth"
  1642. };
  1643. string pindata = JsonConvert.SerializeObject(authRequest, Formatting.None);
  1644. byte[] outinfo = new byte[4096];
  1645. IntPtr retPtr = NationEcTrans(currentConfig.FACE_SERVICE_URL, pindata, ref outinfo[0]);
  1646. string businessResultCode = Marshal.PtrToStringAnsi(retPtr).Trim().Replace("\u0000", "");
  1647. string outstr = Encoding.Default.GetString(outinfo).Trim().Replace("\u0000", "");
  1648. // 检查DLL调用是否成功
  1649. if (businessResultCode != "0000")
  1650. {
  1651. result.Add("code", 1001);
  1652. result.Add("message", $"DLL调用失败,返回码: {businessResultCode}");
  1653. result.Add("type", "face");
  1654. result.Add("device", "江苏医保刷脸认证器(NationECCode.dll)");
  1655. result.Add("businessType", biztype);
  1656. result.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  1657. result.Add("interfaceVersion", "江苏医保刷脸v1.0(与泰和完全一致)");
  1658. result.Add("dllReturnCode", businessResultCode);
  1659. result.Add("dllRawResponse", outstr);
  1660. return result;
  1661. }
  1662. ReadCardResult tmp = Newtonsoft.Json.JsonConvert.DeserializeObject<ReadCardResult>(outstr);
  1663. if (tmp.code == 0)
  1664. {
  1665. FaceAuthData authData = Newtonsoft.Json.JsonConvert.DeserializeObject<FaceAuthData>(tmp.data.ToString());
  1666. return getUserInfoByAuthNo(biztype, authData.authNo);
  1667. }
  1668. else
  1669. {
  1670. result.Add("code", 1001);
  1671. result.Add("message", tmp.message ?? "刷脸授权获取失败");
  1672. result.Add("type", "face");
  1673. result.Add("device", "江苏医保刷脸认证器(NationECCode.dll)");
  1674. result.Add("businessType", biztype);
  1675. result.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  1676. result.Add("interfaceVersion", "江苏医保刷脸v1.0(与泰和完全一致)");
  1677. result.Add("dllReturnCode", businessResultCode);
  1678. result.Add("dllRawResponse", outstr);
  1679. result.Add("authStep", "第一步:获取授权码失败");
  1680. return result;
  1681. }
  1682. }
  1683. catch (Exception ex)
  1684. {
  1685. result.Add("code", 1001);
  1686. result.Add("message", $"江苏医保刷脸操作异常: {ex.Message}");
  1687. result.Add("type", "face");
  1688. result.Add("device", "江苏医保刷脸认证器(NationECCode.dll)");
  1689. result.Add("businessType", biztype);
  1690. result.Add("timestamp", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
  1691. result.Add("interfaceVersion", "江苏医保刷脸v1.0(与泰和完全一致)");
  1692. result.Add("exception", ex.GetType().Name);
  1693. result.Add("exceptionMessage", ex.Message);
  1694. return result;
  1695. }
  1696. }
  1697. /// <summary>
  1698. /// 根据authNo获取用户信息 - 使用NationECCode.dll与泰和完全一致
  1699. /// </summary>
  1700. /// <param name="biztype">业务类型</param>
  1701. /// <param name="authNo">认证号</param>
  1702. /// <returns>用户信息</returns>
  1703. private static JObject getUserInfoByAuthNo(string biztype, string authNo)
  1704. {
  1705. JObject result = new JObject();
  1706. try
  1707. {
  1708. // 使用写死的江苏医保刷脸配置参数
  1709. if (null == currentConfig)
  1710. {
  1711. currentConfig = new JiangSuConfig();
  1712. }
  1713. // 第二步:根据authNo获取医保身份(cn.nhsa.auth.check)
  1714. // 补全所有参数,即使为空也带上
  1715. var data = new {
  1716. businessType = biztype,
  1717. authNo = authNo,
  1718. deviceType = "",
  1719. orgId = currentConfig.FACE_ORG_ID,
  1720. outBizNo = "",
  1721. operatorId = "",
  1722. operatorName = "",
  1723. officeId = "",
  1724. officeName = ""
  1725. };
  1726. var checkRequest = new {
  1727. data = data,
  1728. orgId = currentConfig.FACE_ORG_ID,
  1729. transType = "cn.nhsa.auth.check"
  1730. };
  1731. string pindata = JsonConvert.SerializeObject(checkRequest, Formatting.None);
  1732. byte[] outinfo = new byte[4096];
  1733. IntPtr retPtr = NationEcTrans(currentConfig.FACE_SERVICE_URL, pindata, ref outinfo[0]);
  1734. string businessResultCode = Marshal.PtrToStringAnsi(retPtr).Trim().Replace("\u0000", "");
  1735. string outstr = Encoding.Default.GetString(outinfo).Trim().Replace("\u0000", "");
  1736. ReadCardResult tmp = Newtonsoft.Json.JsonConvert.DeserializeObject<ReadCardResult>(outstr);
  1737. if (tmp.code == 0)
  1738. {
  1739. result.Add("code", 200);
  1740. result.Add("message", "刷脸获取医保身份成功。");
  1741. result.Add("type", "face");
  1742. result.Add("data", outstr);
  1743. result.Add("sign", null);
  1744. }
  1745. else
  1746. {
  1747. result.Add("code", 1001);
  1748. result.Add("message", tmp.message ?? "获取医保身份失败");
  1749. }
  1750. }
  1751. catch (Exception ex)
  1752. {
  1753. result.Add("code", 1001);
  1754. result.Add("message", $"获取医保身份异常: {ex.Message}");
  1755. }
  1756. return result;
  1757. }
  1758. #endregion
  1759. }
  1760. }