# 一个DLL适配几十家医院:医保接口的全局开关设计
一个DLL适配几十家医院医保接口的全局开关设计非科班野生程序员深耕政务信息化20年。医保接口是医院HIS系统里最复杂的对接工作——每个省、每个城市、甚至同省不同医院社保卡读法不同、交易流程不同、结算逻辑不同。东软给了一个基础版的SiInterface.dll但每家医院都要定制。我在这个DLL上设计了一套全局开关交易码路由的适配层让一个DLL通过配置文件就能适配不同医院的不同需求。这个DLL在跨省异地就医直接结算的国家试点中按时保质上线经受住了实战检验。这篇文章讲的就是这个设计。最后感谢豆包、智谱、OpenCode决策是我做的代码是我搓的文字是他们总结的。问题医院HIS系统要对接医保中心标准流程是这样的HIS系统 → SiInterface.dll → 医保中心服务器DLL暴露的接口很简单DLLFUNCintPASCAL EXPORTINIT(char*pOutputInfo,char*in1,char*in2);DLLFUNCintPASCAL EXPORTBUSINESS_HANDLE(constchar*inputData,char*outputData);DLLFUNCintPASCAL EXPORTReadSiCard(char*outputData);HIS调INIT初始化调BUSINESS_HANDLE做业务交易调ReadSiCard读社保卡。inputData的格式是^分隔的字符串交易码^医院编号^操作员^业务年度号^医院交易流水号^中心编码^业务参数^1^交易码决定做什么事交易码含义2100读卡获取个人信息2210门诊挂号/住院登记2310费用明细上传2410结算2420预结算1100医保目录下载1300信息批量下载9100签到看起来很标准。问题在于——每家医院的需求不一样有的医院要读卡器有的不要有的医院是省内医保有的要支持跨省有的医院结算前要先预结算有的直接结算有的医院用新版卡CPU卡有的用旧版卡M1卡有的医院交易码要加密传输如果每家医院一个分支代码就炸了。我的设计全局开关 INI配置 交易码路由1. INI配置文件做全局开关SiInterface.ini里每个配置项都是一个开关[CONNECT] HISCODE 1001 USERNAME ****** PASSWORD ****** [CARD] COM 2 CARDTYPE CPU USECARDREADER 1 USECARD 1 ISDEFPIN 1 DEFPIN 000000 [Server] Host 10.xx.xx.49 Port 8080 WebAppName eapdomain [Chs] Host 10.xx.xx.50 Port 8081DLL初始化时读INI所有差异化逻辑都通过配置控制// 读卡器端口m_CommTool.GetProfileVal(PARA_FILE_INI,CARD,COM,1,strPortNo);m_CardOpter.m_PortNoatol(strPortNo);// 医院编号m_CommTool.GetProfileVal(PARA_FILE_INI,CONNECT,HISCODE,1001,strHisCode);m_strHospNostrHisCode;// 省内医保服务器地址m_CommTool.GetProfileVal(PARA_FILE_INI,Server,Host,10.xx.xx.49,sIp);m_ipsIp;m_CommTool.GetProfileVal(PARA_FILE_INI,Server,Port,8080,sPort);m_portatoi(sPort);// 跨省医保服务器地址另一套服务器m_CommTool.GetProfileVal(PARA_FILE_INI,Chs,Host,10.xx.xx.49,sIp);m_ip_chssIp;m_CommTool.GetProfileVal(PARA_FILE_INI,Chs,Port,8080,sPort);m_port_chsatoi(sPort);同一家医院只要改INI文件不用改代码。2. 双服务地址省内 vs 跨省HttpClient里维护两套连接——省内走m_ip:m_port跨省走m_ip_chs:m_port_chsclassCHttpClient{CInternetSession*cSession;// 省内会话CHttpConnection*pServer;// 省内连接CInternetSession*cSession_chs;// 跨省会话CHttpConnection*pServer_chs;// 跨省连接CString sServerName,sServerName_chs;};初始化时同时建两套连接client.SetServerInfo(m_ip,m_port);// 省内client.SetServerInfoChs(m_ip_chs,m_port_chs);// 跨省交易时根据交易码或参数决定走哪套。3. 交易码路由两位操作码转四位交易码不同医院的HIS系统传的交易码格式不一样。有的传四位标准码如2100有的传两位简码如19。我在BusiHandler入口做了路由转换if(strHisInputData.GetLength()2){oldstrHisInputData.Left(3);}intposold.Find(|,0);if(pos!-1){CString opold.Left(2);strHisInputDatastrHisInputData.Mid(3);CTime tmCTime::GetCurrentTime();CString atm.Format(%Y%m%d%H%M%S%W);if(strcmp(op,17)0){// 17 → 1300 目录下载strHisInputData1300^m_strHospNo^m_strHospNo01^m_busi_no^a^0^type1|19010101010101|^1^;}elseif(strcmp(op,19)0){// 19 → 1310 信息批量下载strHisInputData1310^m_strHospNo^m_strHospNo01^m_busi_no^a^0^type1||||||||^1^;}else{// 其他两位码直接转四位标准格式strHisInputDataop^m_strHospNo^m_strHospNo01^m_busi_no^a^0^strHisInputData|^1^;}}不同医院的HIS系统不需要改调用方式DLL内部统一转成标准格式。4. 跨省标识isYd 控制结算分支有的医院是跨省定点医院结算流程和省内完全不同——要先做预结算2420拆分个人账户和统筹基金再做正式结算2410。我用isYd全局变量控制// 跨省定点医院结算时先预结算再正式结算if(isYd10000atoi(strTransCode)2410atoi(strCenterCode)9999){// 先调2420预结算CString myinput2420^strPart[1]^strPart[2]^strPart[3]^strPart[4]^strPart[5]^strPart[6]^1^;iRetBusiHandler(myinput,strOutputInfo);// 拆分预结算结果个人账户金额、统筹基金、交易金额strAkc264mystrPart3[1];// 个人账户strAkc260.Format(%.2f,atof(mystrPart3[2])atof(mystrPart3[3]));// 统筹// 调用社保卡扣款CString payinfostrAkc264|strAkc260|m_strTime|;iRetDoDebit(payinfo,strOutputInfo);// 再调2410正式结算iRetCallCenterServer(strHisInputDatam_MacAddr|4|^,strOutputInfo);// 最后调1902交易结算验证if(yz1){CString para1902^strHospNo^strOperater^strBusiNo^strHisTransNo^9^strParm^1^;BusiHandler(para,info3);}}省内医院直接2410结算。跨省医院2420预结算→卡扣款→2410正式结算→1902验证。同一个交易码走完全不同的流程。5. 新旧版适配isOld 控制初始化路径有的医院还在用老版本的医保系统接口参数不同。INIT时通过第二个参数判断intCInterfaceBusiHandler::Init(char*pOutputInfo,char*in1,char*in2){CString in;in.Format(%s,in1);if(strcmp((null),in)0||in.IsEmpty()||in.GetLength()3){isOldfalse;// 新版iRetm_CSSMode.Init(strErrMsg);}else{isOldtrue;// 老版iRetm_CSSMode.Init(strErrMsg,true);}}老版初始化时还要自动签到if(bForCSMode){// 老版CString command9100^m_strHospNo^m_strHospNo01^^200705081041082002727^0^|^1^;intiBusiHandler(command,out);if(i0){// 解析签到结果获取业务年度号m_busi_nostrPart[2];}}6. 交易码加密DES动态解密有的省份要求交易码加密传输超过7位的交易码是加密后的if(strTransCode.GetLength()7){strTransCodedes(strHospNo,strTransCode);// 用医院编号做密钥解密}DES函数用医院编号的所有字符ASCII值求和取模127得到一个字节密钥然后逐字节异或chargetKey(CString hosp){charkey0;intsum0;char*arrhosp.GetBuffer(hosp.GetLength());for(inti0;ihosp.GetLength();i){sumsumarr[i];}keysum%127;returnkey;}7. 地域前缀HK开头走老版接口有的地区还在用老版医保接口交易码以地区编码前缀开头如HK四位操作码。DLL内部需要走不同的初始化和交易流程if(strcmp(strHisInputData.Left(2),HK)0){CString opstrHisInputData.Left(4);strHisInputDatastrHisInputData.Mid(5);// 按标准格式组装strHisInputDataop^m_strHospNo^m_strHospNo01^m_busi_no^a^0^strHisInputData|^1^;}最终效果SiInterface.dll ┌──────────────┐ HIS(医院A) ─── 17|1 ──→ │ │ ──→ 省内医保服务器(10.xx.xx.49:8080) HIS(医院B) ─── 2100^ ──→│ 交易码路由 │ ──→ 省内医保服务器 HIS(医院C) ─── 2410^ ──→│ 全局开关 │ ──→ 跨省医保服务器(10.xx.xx.50:8081) HIS(医院D) ─── HK1148^→ │ 新旧版适配 │ ──→ 跨省医保服务器 │ 交易码解密 │ └──────────────┘ ↑ SiInterface.ini (每家医院不同)一个DLL通过不同的INI配置文件适配了几十家医院的差异化需求。部署的时候只需要改配置文件不需要改代码不需要重新编译。决策原则不改代码改配置。医保接口的差异化是医院级的——每家医院的环境不同、版本不同、流程不同。如果用if-else硬编码每加一家医院就要改一次代码、编译一次、测试一次、分发一次。用INI配置做全局开关部署人员改一个文本文件就行。这个思路和书稿第九章MongoDB混合存储的level属性、第十六章SM4加解密的usageCode全局开关是一脉相承的——把差异外置到配置把共性固化在代码。医保接口的对接大家是怎么做的一家医院一套代码还是有统一的适配方案欢迎评论区聊聊。作者许彰午| 非科班野生程序员深耕政务信息化20年标签#VC #医保接口 #DLL #全局开关 #交易码路由 #跨省结算 #政务信息化 #硬件集成