ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

HIS对接上海医保五期接口:16个业务方法详解与联调避坑指南

HIS对接上海医保五期接口:16个业务方法详解与联调避坑指南 简介这是上海五期医保接口的说明文档面向HIS系统开发与维护人员以docx格式打包共1个文件压缩包大小约34KB。文档围绕电子凭证解码、医保项目明细上传等核心业务系统梳理了卡类型、账户标志、费用结算单元、中心流水号、就诊单元号、明细账单号等名词并详细讲解读卡、挂号请求与确认、收费请求与确认、登记/撤销登记、明细上传/撤销、住院结算、对账退款、账户/登记查询等业务流程。接口描述部分统一采用JSON返回报文格式逐一说明初始化、ShYbV5_S000读卡、ShYbV5_SE01解码、挂号请求、结算等方法的参数与返回字段便于开发时直接对照编码。目前已有1592人学习下载适合正在对接上海医保五期接口的研发人员阅读参考可快速弄清医保接口调用关系、报文结构和关键字段含义减少联调阶段的对接成本。1. 上海医保五期接口HIS对接医保中心时绕不开的那道门做医院信息系统HIS的同行应该都有体会医保接口这东西平时不显山不露水一上线就变成全院最紧张的那根弦。上海医保五期接口是HIS与医保中心系统之间数据交互的规范前台程序调用ShYbV5.dll完成读卡、挂号、收费、住院登记、明细上传、结算对账这一整条链路。这份说明文档的价值在于它把五期接口的16个业务方法、字段定义、返回格式全部列清楚了是HIS厂商开发、医院信息科验收时的手边资料。适合正在做上海医保对接的HIS开发工程师、实施人员和医院信息科运维。读完你能知道接口怎么调用、参数怎么传、返回怎么解析以及联调时最容易在哪些地方翻车。2. 读懂五期接口的设计四个关键概念与16个业务方法2.1 卡类型三分芯片卡、磁条卡、电子凭证的取数路径完全不同文档开篇就给了一个容易忽略的定义卡类型分为芯片卡1保障卡或者IC卡、磁条卡0、电子凭证3。三种卡在对接代码里的走法完全不一样这是后续所有业务方法入参的基础。芯片卡需要插入读卡机之后调用ShYbV5_S000获取病人基本信息磁条卡共28位直接在读卡机上刷卡操作即可获取病人卡号电子凭证则需要通过支付宝、微信、随申码等渠道获取二维码然后调用ShYbV5_SE01解码拿到病人身份证信息以及ectoken令牌才能继续。换句话说写代码时第一件事不是调挂号接口而是先把cardtype的分支逻辑切干净。// 伪代码示意按卡类型分流取卡号 string cardtype GetCardTypeFromDevice(); // 从读卡设备读到的卡类型 string carddata string.Empty; if (cardtype 1) { // 芯片卡插入读卡机调用S000直接返回病人信息 string json ShYbV5_S000(); var patient JsonConvert.DeserializeObjectPatientInfo(json); carddata patient.kh; // 9位保障卡卡号 } else if (cardtype 0) { // 磁条卡读卡机直接刷出28位卡号不需要调S000 carddata ReadMagCardFromDevice(); // 28位 } else if (cardtype 3) { // 电子凭证二维码解码拿到ectoken作为后续carddata string qr GetQrCodeFromChannel(alipay); string json ShYbV5_SE01(qr, 1, 01, deptId, deptName); var token JsonConvert.DeserializeObjectECTokenResult(json); carddata token.ecToken; }这段分支逻辑的逻辑说明芯片卡场景下carddata用S000返回的kh磁条卡场景下直接用读卡机读出的28位卡号电子凭证场景则用SE01解码返回的ecToken三种来源不同不能混用。参数说明deptId和deptName是医保科室编码和名称在电子凭证解码时必须传后续业务方法里也要保持一致否则医保中心会校验科室信息失败。这里踩过一次坑有人把电子凭证的二维码原文直接当成carddata传给挂号接口医保中心直接返回「令牌无效」后来改成用SE01返回的ecToken才通过。所以对接时脑子里要绷一根弦carddata这个字段在不同卡类型下代表的意义是完全不同的。2.2 账户标志与医保支付办法先搞懂钱怎么分才能看懂返回的金额字段账户标志是文档里比较绕的概念。每位医保病人医保中心系统通过16位的账户标志描述其人员类别每一位数字都有对应定义涵盖职退情况、保健情况、特殊人员标识、医保办法等详见《中心系统与定点医疗机构接口规范 5.0》。HIS这边一般不用去解析这16位但要在界面上原样展示或者留存因为后续对账可能需要。真正影响开发的是医保支付办法它决定了挂号、结算返回里那些金额字段的语义。文档里对不同人员类别的支付规则做了说明我整理成了表格人员类别支付规则门诊普通先用当年账户再用历年账户再进入自负段自负累计满后按医院级别一、二、三分别自负10%、15%、20%其余由医保支付门诊大病统筹支付封顶线以下个人自负8%现金部分可用历年账户抵充其余医保统筹支付封顶线以上个人自负20%其余由医保附加段/地方附加段支付家庭病床个人自负20%其余由医保支付住院起付段以下个人自负以上自负8%其余医保支付统筹支付封顶线以上个人自负20%其余由医保附加段/地方附加段支付搞懂这个表格再去看接口返回就清楚多了。比如SH01挂号请求的返回里curaccountpay是当年账户支付数hisaccountpay是历年账户支付数zfdxjzfs是自负段现金支付数tcdzhzfs是统筹段账户支付数fjzfs是附加支付数——这些字段跟支付规则里的每个段是一一对应的。HIS界面要展示「医保支付多少、个人支付多少」时直接取这些字段就行不要自己在本地按比例重算。医保中心算出来的才是结算依据本地重算既容易跟医保对不上账也容易在窗口被病人质疑。2.3 计算申请序号、就诊单元号、中心流水号三个编号的职责边界接口交互里有两组编号特别容易混第一组是计算申请序号、就诊单元号、中心流水号。文档的定义是HIS方每次向医保中心系统发送结算请求时中心系统返回唯一申请序号称为计算申请序号每次确认结算时产生一个费用结算单元中心系统生成唯一流水号即中心流水号就诊单元是将医疗服务过程按特定参数划分后产生的编号一般由挂号或住院登记等交易产生。这三个编号的依赖关系是挂号请求SH01先返回jssqxh计算申请序号挂号确认SH02带着jssqxh去换jzdyh就诊单元号和lsh中心流水号。所以代码里要么把jssqxh存在事务上下文里要么请求和确认放同一个方法内完成不要跨操作缓存复用。第二组容易混的是明细账单号和费用明细单体序号。明细账单号由请求明细上传发起、中心系统生成且唯一费用明细单体序号是每个明细账单里的费用明细由本地系统依次编号从1开始在一个明细账单内保持唯一。结构上是典型的一对多一个明细账单号下面挂着多个费用明细单体序号。后续做明细撤销、收费请求时都要引用这两个编号。2.4 16个业务方法怎么分组从读卡到对账的完整闭环文档第二章列了16个功能点看起来很多但按业务链路分组后就很清晰分组业务方法读卡与解码读卡、解码挂号挂号请求和确认普通、大病、工伤收费收费请求和确认普通、大病、家床、工伤登记与撤销登记请求、撤销登记住院、急观、家床、保健急观、保健入院明细明细上传普通、保健、明细撤销结算与查询住院结算普通、工伤、对账请求、退款请求、账户查询、登记查询、干保查询、工伤认定号查询、交易查询这16个方法对HIS主程序来说像一个黑匣子按约定调用、传约定字段、收JSON返回。但联调时要特别注意一条规律挂号、收费、登记这三条主链路都是「请求→确认」两步走不能跳步也不能把上一步的返回参数用错位置。后面第三章我会把具体方法的调用细节拆开讲。3. 核心方法调用实战从Init到明细上传的完整链路3.1 Init()每个操作窗口前的第一个动作Init是公共初始化方法签名是Public bool Init()参数有三个czybm操作员编码20位字符串、czyxm操作员姓名100位字符串、jyqd交易渠道2位字符串10线下、20线上。返回true为初始化成功、false为失败。文档里有一句容易被忽略的话本方法需要在每个独立的操作前调用。意思是Init不是只在程序启动时调一次而是每个业务窗口开始操作前都要调。举例来说挂号窗口在进行挂号请求的时候调用Init那么挂号确认时无需再调用。这个语义要跟HIS前端的窗口生命周期对齐否则容易出现操作员信息没传、医保中心返回操作员不存在。常见做法是在窗口打开或进入业务操作前统一封装一个初始化入口public bool EnsureMedicareInit(string czybm, string czyxm) { // 每个独立操作前检查一次交易渠道线下默认传10 bool ok ShYbV5.Init(czybm, czyxm, 10); if (!ok) { // Init失败时优先确认操作员是否有医保权限、dll是否注册 Logger.Error($医保初始化失败, operator{czybm}); } return ok; }逻辑说明这里把Init单独封装是为了防止每个窗口各写一遍初始化代码、参数不统一。参数说明czybm和czyxm必须跟HIS登录人的工号和姓名一致jyqd默认传10线下如果HIS有线上挂号场景才传20。联调中Init失败通常就是两类原因操作员编码在医保中心侧没开通或者ShYbV5.dll没有用管理员权限注册成功。这两件事排查起来都很快但容易在环境切换时反复出现。3.2 读卡与解码ShYbV5_S000和ShYbV5_SE01的两条取数路径ShYbV5_S000用于读取保障卡信息无参数直接返回JSON字符串。文档给的示例报文是这样的{ kh: Z59898D78, xm: test, xb: 1, sfzh: 31000119500417017, lxdh: 13333333333, txdz: 上海市闵行区虹梅路, yzbm: , xzqh: 310000 }字段含义kh卡号9位、xm姓名50位、xb性别1位、sfzh身份证号18位、lxdh联系电话15位、txdz通讯地址80位、yzbm邮政编码6位、xzqh行政区域代码6位。注意yzbm在报文里是空字符串反序列化时要做空值容忍不能一上去就ToString()。ShYbV5_SE01用于电子凭证解码参数较多参数说明最大长度ecQrcode电子凭证二维码值80ecQrChannel获取二维码渠道1支付宝、2微信、3随申码1businessType用码业务类型见字段表10officeId医保科室编码50officeName医保科室名称50返回字段里最重要的三个是username姓名、idno证件号码、idtype证件类型以及ecToken令牌。ecToken的有效期是联调时最容易踩的坑后面第四章单独说。3.3 挂号请求ShYbV5_SH01参数多、类型杂逐项拆开看SH01是普通、大病、工伤挂号统一的请求方法参数是五期接口里最多的一组参数说明最大长度cardtype卡类型0磁条卡、1芯片卡、3电子凭证1carddata卡号磁条卡28位、芯片卡为空、电子凭证填令牌63deptid科室编码50zlxmdm诊疗项目代码50personspectag特殊人员标识0普通、1离休、2伤残、3干保1yllb医疗类别3dbtype大病项目代码大病挂号填写其他可空1persontype病人类型0普通、1工伤1gsrdh工伤认定号工伤挂号填写其他为空10totalexpense交易费用总额数字格式ybjsfwfyze医保结算范围总额数字格式zhenlf诊疗费数字格式ghf门急诊诊疗费自费数字格式fybjsfwfyze非医保结算范围总额数字格式jmbz享受社区减免标志0不享受、1享受1这里有几个容易被绕晕的填写规则。chip卡场景carddata传空字符串因为S000在读卡环节已经拿到了卡号再传一次反而可能被医保中心认为数据不一致电子凭证场景carddata传SE01返回的ecToken而非二维码原文磁条卡场景直传28位卡号。条件字段要按挂号类型填工伤挂号必须带gsrdh和persontype1大病挂号必须带dbtype普通挂号这些字段传空即可。SH01返回字段更多但核心是这几个accountattr账户标志16位、jssqxh计算申请序号34位、jlc记录测号12位、curaccountpay当年账户支付数、hisaccountpay历年账户支付数、zfdxjzfs自负段现金支付数、tcdzhzfs统筹段账户支付数、tcdxjzfs统筹段现金支付数、tczfs统筹支付数、fjdzhzfs附加段账户支付数、fjdxjzfs附加段现金支付数、fjzfs附加支付数、jfje减负金额。界面上展示医保支付金额时直接把这些字段映射过去。3.4 挂号确认ShYbV5_SH02用计算申请序号换就诊单元号SH02的参数只有一个jssqxh就是SH01返回的计算申请序号。返回两个有效字段jzdyh就诊单元号20位、lsh中心流水号16位。调用时机的关键点在于SH02必须在SH01发起之后、进入下一个业务动作之前执行。挂号确认成功后就诊单元号就产生了后续的收费请求、明细上传都要带着jzdyh。示例代码// 挂号请求 string reqJson ShYbV5_SH01( cardtype, carddata, deptid, zlxmdm, personspectag, yllb, dbtype, persontype, gsrdh, totalexpense, ybjsfwfyze, zhenlf, ghf, fybjsfwfyze, jmbz); var req JsonConvert.DeserializeObjectSH01Result(reqJson); if (req.jssqxh.IsNullOrEmpty()) { throw new Exception(挂号请求未返回计算申请序号); } // 挂号确认用jssqxh换jzdyh和lsh string confirmJson ShYbV5_SH02(req.jssqxh); var confirm JsonConvert.DeserializeObjectSH02Result(confirmJson); string jzdyh confirm.jzdyh; // 就诊单元号后续收费/明细上传要用 string lsh confirm.lsh; // 中心流水号逻辑说明请求和确认拆成两步是因为医保中心在请求阶段先做费用预计算、在确认阶段才正式落地业务数据SH01返回的jssqxh相当于这笔交易的临时凭证SH02拿着它去落地。参数说明每次挂号必须重新请求拿新jssqxh不能复用上次的值如果SH02报「申请序号不存在」多半是用了缓存里的旧序号。3.5 明细上传ShYbV5_SN01收费前的数据准备明细上传是收费请求的前置步骤文档里有一个明确要求在调用明细上传方法之前HIS系统需要提供一个查询病人费用明细的接口接口建议使用webservice返回JSON格式字符串。这意味着HIS侧要先把费用明细数据准备好并且字段结构对齐到SN01需要的输出格式。SN01的入参比较简单djh一个字段门诊填门诊号、住院填住院号。输出字段则是一大串明细结构xh序号、cfh处方号、deptid科室编码、deptname科室名称、cfysh处方医生号、cfysxm处方医生姓名、fylb费用类别、mxxmbm明细项目编码、mxxmmc明细项目名称、mxxmdw明细项目单位、mxxmdj明细项目单价、mxxmsl明细项目数量、mxxmje明细项目金额、mxxmjyfy明细项目交易费用、mxxmybjsfwfy明细项目医保结算范围费用以及yyclpp医用材料品牌、zczh注册证号、mxxmgg规格、mxxmsyrq使用日期、bxbz报销标志、sftfbz收费退费标志、jfbz减负标志、sfxfmx是否细分明细。其中sfxfmx为1时表示这条明细下面嵌套着子类明细用xh关联上一层明细序号{ djh: MZ20240001, fhm: 0, fhxx: 成功, mxzdh: MX202400001234, items: [ { xh: 1, cfh: CF001, deptid: 1001, deptname: 心血管内科, cfysh: Y001, cfysxm: 张医生, fylb: 01, mxxmbm: 110100001, mxxmmc: 诊查费, mxxmdj: 20, mxxmsl: 1, mxxmje: 20, mxxmybjsfwfy: 20, sftfbz: 1, sfxfmx: 1, xfmx: [ { xh: 1, mxxmbmzl: 11010000101, mxxmmczl: 诊查费-普通, mxxmdjzl: 20, mxxmslzl: 1, mxxmjezl: 20, sftfbzzl: 1 } ] } ] }这个嵌套结构在反序列化时要用对应的子类集合来承载否则上传后医保中心返回的明细账单号对不上明细内容。明细上传成功后返回mxzdh明细账单号后续收费请求、明细撤销都要带上这个号。4. 联调避坑与医保中心对接时踩过的7个坑4.1 坑芯片卡返回的kh字段与磁条卡卡号被当成同一种东西现象同一病人用芯片卡挂号和磁条卡挂号传给SH01的carddata一个空、一个28位卡号但是界面上展示的病人信息有时对、有时不对。原因芯片卡的kh来自S000读卡返回是9位保障卡卡号磁条卡是读卡机直接读出的28位卡号。两者在医保中心侧对应的卡片索引不同混用会导致信息错位。解决按cardtype分支处理芯片卡场景carddata传空磁条卡场景才传28位卡号。对接代码里把「读卡」和「取卡号」两个动作拆开不要统一走一个入口。4.2 坑电子凭证的ecToken过期后续操作报「令牌无效」现象SE01解码成功拿到ecToken但窗口排队超过几分钟后再发起挂号请求医保中心返回令牌无效或者会话超时。原因ecToken是带有效期的令牌不是永久的挂号窗口排队时间一长就超时。解决我一般会把SE01封装成带刷新机制的取令牌函数每次发起挂号或收费前先检查令牌是否在有效期内过期就重新解码一次。如果业务上允许可以在收到「令牌无效」返回时自动重试一次SE01再发起原请求这个重试成本很低。4.3 坑磁条卡读出的数据带上了起始符、结束符和校验位现象刷卡后程序拼出来的carddata偶发长度不是28位医保中心返回卡号错误。原因读卡机在刷卡时会把磁道数据里的起始符、结束符、校验位一并读出文档明确说磁条卡共28位多余的字符在校验时直接报错。解决对读卡机返回的原始字符串做清洗截取中间连续28位作为carddata。清洗逻辑放在刷卡后立刻处理不要拖到SH01请求前才做否则出错时变量太多不好排查。4.4 坑jssqxh被缓存复用二次使用时提示「申请序号不存在」现象挂号请求成功确认时报错或者确认成功后同一窗口又做一次确认医保中心拒绝。原因计算申请序号是每次结算请求时唯一生成的用完即失效。把上一次的值缓存下来复用属于典型的并发窗口问题。解决请求与确认放进同一个事务方法里jssqxh用局部变量传递不放进窗口级的成员变量。前端需要重试时先重新发起请求拿新jssqxh再确认。4.5 坑明细上传的退费标志和金额符号搞反现象退费明细上传后医保中心返回金额校验失败或者把退费当成了收费。原因明细里有sftfbz收费退费标志1收费、2退费。退费项目只把标志改成2、金额仍然填正数医保中心会认为金额符号和标志矛盾。解决退费明细要把sftfbz置为2金额按实际退费方向处理。这个规则在联调阶段就要用真实退费数据验证一遍别等上线后才发现。4.6 坑交易费用总额与明细金额之和对不上现象SH01的totalexpense手填了一个数后面SN01上传的mxxmje加起来跟totalexpense不一致收费请求时医保中心返回金额不匹配。原因totalexpense交易费用总额、ybjsfwfyze医保结算范围总额、fybjsfwfyze非医保结算范围总额这三个金额字段语义不同很多人直接把发票金额填给totalexpense漏了医保与非医保的分拆。解决按「totalexpense ybjsfwfyze fybjsfwfyze」组织数值。HIS侧费用数据里如果有非医保项目在传参前先做拆分计算保证两个子项加起来与总额一致。4.7 坑测试数据不带大病或工伤标识返回的支付段不完整现象联调用普通测试病人调大病挂号接口返回里没有统筹段和附加段的金额字段界面展示缺数据。原因医保中心按病人的人员类别、账户标志和挂号类型决定返回哪些支付字段测试数据本身不是大病或工伤人员自然不会走对应的支付段。解决联调大病和工伤场景时用医保中心提供的对应测试人员卡号并在SH01里正确传dbtype、persontype和gsrdh。如果返回字段缺失先检查入参条件字段而不是怀疑接口有bug。5. 上线前验证顺序与检查清单5.1 联调环境的交易验证顺序我一般会把五期接口的联调按这个顺序推进每步通过了再进下一步第一步验证Init操作员编码和交易渠道能返回true第二步验证取数链路芯片卡S000、磁条卡直接取28位、电子凭证SE01解码三种方式分别跑一遍第三步验证挂号链路SH01拿jssqxh、SH02拿jzdyh和lsh重点看特殊人员标识和工伤字段第四步验证明细上传用真实处方走SN01拿mxzdh顺便把项目编码映射验证掉第五步验证收费请求与确认确认通过后核对该笔交易的金额字段与HIS前端展示是否一致最后是对账、退款、交易查询闭环通了才算完。5.2 返回JSON解析的三个稳定做法五期接口所有返回string类型的方法都是标准JSON但解析时有三个细节值得坚持。第一金额字段用decimal解析不要用double避免分位精度丢失。文档里标注「数字格式」的字段都是金额double在累计计算时会产生误差。public class SH01Result { public string jssqxh { get; set; } // 计算申请序号 public decimal curaccountpay { get; set; } // 当年账户支付数 public decimal tcdzhzfs { get; set; } // 统筹段账户支付数 public decimal fjzfs { get; set; } // 附加支付数 }第二字符串字段允许null和空串示例报文里yzbm就是空字符串反序列化时做好容错。第三返回码不能只看一个字段明细上传时错误可能挂在明细子项上解析时要同时记录fhm、fhxx和明细级别的状态方便定位具体是第几条明细被拒。5.3 现在我会在对接前先做的事对接过几轮医保接口之后我现在养成了一个习惯拿到接口文档后不急着写代码先做两件事。第一件是把文档里所有字段整理成一份字段字典标注来源接口、类型、长度、是否可空、取值样例这份字典同时给开发、测试和医院信息科使用省得联调时各说各话。第二件是提前准备联调用例覆盖芯片卡、磁条卡、电子凭证三种取数路径以及普通挂号、大病挂号、工伤挂号三种业务类型到了医保中心窗口期内一天就能跑完。这个顺序救过我很多次。希望帮到你。本文还有配套的精品资源点击获取
返回列表