From 108e6c4152f3eff0dc4973a259702840ec44dc78 Mon Sep 17 00:00:00 2001 From: xiaodaozi Date: Sat, 25 Oct 2025 19:05:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E4=BA=91=E5=8D=97=E7=9C=81?= =?UTF-8?q?=E5=B1=80=E5=8C=BB=E4=BF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/lib/command.bat | 2 + doc/lib/hsaf-common-1.0.0-SNAPSHOT.jar | Bin 0 -> 96364 bytes pom.xml | 7 + .../com/dpkj/common/config/YnChsConfig.java | 27 +- .../dpkj/common/constant/CharsetConst.java | 3 +- .../hischs/controller/HispayController.java | 13 +- .../chs/hischs/service/IHispayService.java | 9 +- .../service/impl/HispayServiceImpl.java | 307 +++++++------ .../chs/ynchs/constant/YnchsConst.java | 38 ++ .../ynchs/controller/YnChsPayController.java | 20 +- .../modules/chs/ynchs/dll/YnChsPayDll.java | 11 +- .../chs/ynchs/service/IYnChsPayService.java | 30 -- .../ynchs/service/IYnchspayByDllService.java | 21 + .../service/IYnchspayByWebapiService.java | 12 + .../service/impl/YnChsPayServiceImpl.java | 185 -------- .../impl/YnchspayByDllServiceImpl.java | 234 ++++++++++ .../impl/YnchspayByWebapiServiceImpl.java | 233 ++++++++++ .../dpkj/modules/chs/ynchs/utils/BCUtils.java | 39 ++ .../modules/chs/ynchs/utils/EasyGmUtils.java | 361 +++++++++++++++ .../chs/ynchs/utils/HseEncAndDecUtil.java | 278 +++++++++++ .../modules/chs/ynchs/utils/SignUtil.java | 186 ++++++++ .../modules/chs/ynchs/utils/StringUtil.java | 430 ++++++++++++++++++ src/main/resources/application-dev.yml | 25 +- src/main/resources/application-pro.yml | 12 +- 24 files changed, 2082 insertions(+), 401 deletions(-) create mode 100644 doc/lib/hsaf-common-1.0.0-SNAPSHOT.jar create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/constant/YnchsConst.java delete mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/service/IYnChsPayService.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByDllService.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByWebapiService.java delete mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnChsPayServiceImpl.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByDllServiceImpl.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByWebapiServiceImpl.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/utils/BCUtils.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/utils/EasyGmUtils.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/utils/HseEncAndDecUtil.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/utils/SignUtil.java create mode 100644 src/main/java/com/dpkj/modules/chs/ynchs/utils/StringUtil.java diff --git a/doc/lib/command.bat b/doc/lib/command.bat index 2aaf8b3..6f58cf1 100644 --- a/doc/lib/command.bat +++ b/doc/lib/command.bat @@ -3,4 +3,6 @@ echo off :: 添加jar包到本地仓库 cmd /k "mvn install:install-file -Dfile=jacob-1.21.jar -DgroupId=com.jacob -DartifactId=jacob -Dversion=1.21 -Dpackaging=jar" +:: cmd /k "mvn install:install-file -Dfile=hsaf-common-1.0.0-SNAPSHOT.jar -DgroupId=hsaf.common -DartifactId=hsaf -Dversion=1.0.0-SNAPSHOT -Dpackaging=jar" + pause diff --git a/doc/lib/hsaf-common-1.0.0-SNAPSHOT.jar b/doc/lib/hsaf-common-1.0.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..86e908badc3b9be14cee9da7d855da34278f8dba GIT binary patch literal 96364 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 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index c2d3919..a1adb8c 100644 --- a/pom.xml +++ b/pom.xml @@ -179,6 +179,13 @@ itext-asian 5.2.0 + + + diff --git a/src/main/java/com/dpkj/common/config/YnChsConfig.java b/src/main/java/com/dpkj/common/config/YnChsConfig.java index 5a23c5b..e59e477 100644 --- a/src/main/java/com/dpkj/common/config/YnChsConfig.java +++ b/src/main/java/com/dpkj/common/config/YnChsConfig.java @@ -20,7 +20,7 @@ public class YnChsConfig { private String url; /** - * 医保机构编码 + * 两定医保机构编码 */ private String fixmeDinsCode; /** @@ -43,5 +43,30 @@ public class YnChsConfig { */ private String cityCode; + /** + * 应用ID + */ + private String appid; + /** + * 应用密钥 + */ + private String appKey; + /** + * 应用名称 + */ + private String appName; + /** + * 渠道私钥 + */ + private String channelPrivateKey; + /** + * 渠道公钥 + */ + private String channelPublicKey; + /** + * 平台公钥 + */ + private String platformPublicKey; + } diff --git a/src/main/java/com/dpkj/common/constant/CharsetConst.java b/src/main/java/com/dpkj/common/constant/CharsetConst.java index a4a1a35..5603a26 100644 --- a/src/main/java/com/dpkj/common/constant/CharsetConst.java +++ b/src/main/java/com/dpkj/common/constant/CharsetConst.java @@ -9,6 +9,7 @@ public interface CharsetConst { // 中文编码 String GB2312 = "GB2312"; - + // UTF8 + String UTF8 = "UTF-8"; } diff --git a/src/main/java/com/dpkj/modules/chs/hischs/controller/HispayController.java b/src/main/java/com/dpkj/modules/chs/hischs/controller/HispayController.java index 9693464..0108800 100644 --- a/src/main/java/com/dpkj/modules/chs/hischs/controller/HispayController.java +++ b/src/main/java/com/dpkj/modules/chs/hischs/controller/HispayController.java @@ -29,17 +29,16 @@ public class HispayController { private final IHispayService hispayService; - /** * 通过医保卡或医保电子凭证读卡 * * @return com.dpkj.common.vo.Result * @author 萧道子 2025/5/20 */ - @PostMapping("findReadCode") - public Result findReadCode() { + @PostMapping("getUserInfoByChsCode") + public Result getUserInfoByChsCode() { try { - JSONObject res = hispayService.readCode(); + JSONObject res = hispayService.getUserInfoByChsCode(); return Result.ok("成功", res); } catch (Exception e) { e.printStackTrace(); @@ -56,15 +55,15 @@ public class HispayController { * @return com.dpkj.common.vo.Result * @author 萧道子 2025/5/28 */ - @PostMapping("findReadCard") - public Result findReadCard(@RequestBody JSONObject data) { + @PostMapping("getUserInfoByChsCard") + public Result getUserInfoByChsCard(@RequestBody JSONObject data) { try { String password = data.getString("password"); if (StrUtil.isEmpty(password)) { throw new RuntimeException("密码不可为空"); } - JSONObject res = hispayService.readCard(password); + JSONObject res = hispayService.getUserInfoByChsCard(password); return Result.ok("成功", res); } catch (Exception e) { e.printStackTrace(); diff --git a/src/main/java/com/dpkj/modules/chs/hischs/service/IHispayService.java b/src/main/java/com/dpkj/modules/chs/hischs/service/IHispayService.java index 26678bc..8940f3f 100644 --- a/src/main/java/com/dpkj/modules/chs/hischs/service/IHispayService.java +++ b/src/main/java/com/dpkj/modules/chs/hischs/service/IHispayService.java @@ -8,21 +8,20 @@ import com.dpkj.modules.chs.hischs.vo.OutpatientFinalModel; public interface IHispayService { /** - * 通过医保电子凭证读卡 + * 通过医保电子凭证获-取患者身份信息 * * @return com.alibaba.fastjson.JSONObject * @author 萧道子 2025/5/28 */ - JSONObject readCode(); - + JSONObject getUserInfoByChsCode(); /** - * 通过医保卡-读卡 + * 通过医保卡-取患者身份信息 * * @return com.alibaba.fastjson.JSONObject * @author 萧道子 2025/5/28 */ - JSONObject readCard(String password); + JSONObject getUserInfoByChsCard(String password); /** diff --git a/src/main/java/com/dpkj/modules/chs/hischs/service/impl/HispayServiceImpl.java b/src/main/java/com/dpkj/modules/chs/hischs/service/impl/HispayServiceImpl.java index 2c77493..2127db4 100644 --- a/src/main/java/com/dpkj/modules/chs/hischs/service/impl/HispayServiceImpl.java +++ b/src/main/java/com/dpkj/modules/chs/hischs/service/impl/HispayServiceImpl.java @@ -15,7 +15,6 @@ import com.jacob.com.Dispatch; import com.jacob.com.Variant; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.w3c.dom.Document; import javax.annotation.PostConstruct; import java.util.Map; @@ -69,6 +68,150 @@ public class HispayServiceImpl implements IHispayService { // ComThread.Release(); //容易导致线程发生阻塞 } + + @Override + public JSONObject getUserInfoByChsCode() { + /** 1、组装参数 */ + JSONObject params = new JSONObject() + .fluentPut("cardtype", "9") // 1.就诊卡,2.医保卡,5.门诊号,6.患者姓名和电话号码,8.电子健康码/卡,9.医保电子凭证 + .fluentPut("cardno", "") // 患者就诊卡号 + .fluentPut("sfzh", "") // 身份证号 + .fluentPut("hzxm", "") // 患者姓名 + .fluentPut("phone", ""); // 患者电话号码 + String paramsXml = this.handleParams(params); + + /** 2、调用COM函数 */ + log.debug("[HispayServiceImpl][readCode][医保读卡-电子凭证] 接口入参:{}", paramsXml); + Variant vres = new Variant("", true); + Variant call = Dispatch.call(dispatch, "fRun", "BMZXX010", paramsXml, vres); + String resStr = vres.getStringRef(); + log.debug("[HispayServiceImpl][readCode][医保读卡-电子凭证] call返回值:{} 结果:{}", call, resStr); + + + /** 3、处理读卡结果 */ + JSONObject result = verifyResult(resStr); + if (!result.containsKey("item")) { + throw new RuntimeException("item数据为空"); + } + + return result.getJSONObject("item"); + } + + @Override + public JSONObject getUserInfoByChsCard(String password) { + /** 1、组装参数 */ + JSONObject params = new JSONObject() + .fluentPut("cardtype", "2") // 1.就诊卡,2.医保卡,5.门诊号,6.患者姓名和电话号码,8.电子健康码/卡,9.医保电子凭证 + .fluentPut("cardno", "") // 患者就诊卡号 + .fluentPut("sfzh", "") // 身份证号 + .fluentPut("hzxm", "") // 患者姓名 + .fluentPut("phone", ""); // 患者电话号码 + String paramsXml = this.handleParams(params, password); + + /** 2、调用COM函数 */ + log.debug("[HispayServiceImpl][readCard][医保读卡-医保卡] 接口入参:{}", paramsXml); + Variant vres = new Variant("", true); + Variant call = Dispatch.call(dispatch, "fRun", "BMZXX010", paramsXml, vres); + String resStr = vres.getStringRef(); + log.debug("[HispayServiceImpl][readCard][医保读卡-医保卡] call返回值:{} 结果:{}", call, resStr); + + /** 3、处理读卡结果 */ + JSONObject result = this.verifyResult(resStr); + if (!result.containsKey("item")) { + throw new RuntimeException("item数据为空"); + } + + return result.getJSONObject("item"); + } + + + @Override + public ResultData chsCodeAsOutpatientBegin(OutpatientBeginModel data) { + // 用户读卡-生成token + JSONObject userInfo = this.getUserInfoByChsCode(); + String patientId = userInfo.getString("patid"); + data.setPatientId(patientId); + + // 更新常量状态 已读卡 + ChsPayStateConst.put(data.getPrescriptionNo(), 1); + + return this.outpatientBudget(data); + } + + @Override + public ResultData chsCodeAsOutpatientFinal(OutpatientFinalModel data) { + /** 1、组装参数 */ + String requestTime = DateUtil.now(); + data.setPaytime(requestTime); + JSONObject params = ((JSONObject) JSON.toJSON(data)) + .fluentPut("czksfbz", "0") // 是否扣院内账户,与预算保持一致 + .fluentPut("zfjsbz", "0") // 是否自费结算,与预算保持一致,0根据病人医保代码结算,1自费结算 + .fluentPut("ybrc", "") + .fluentPut("ptlsh", "") + .fluentPut("jysm", ""); + String paramsXml = this.handleParams(params); + + /** 2、调用COM函数 */ + log.debug("[HispayServiceImpl][chsCodeAsOutpatientFinal][门诊缴费-结算] 接口入参:{}", paramsXml); + Variant resVariant = new Variant("", true); + Variant call = Dispatch.call(dispatch, "fRun", "BMZJF002", paramsXml, resVariant); + String resStr = resVariant.getStringRef(); + log.debug("[HispayServiceImpl][chsCodeAsOutpatientFinal][门诊缴费-结算] call返回值:{} 结果:{}", call, resStr); + String responseTime = DateUtil.now(); + + /** 3、处理结果 */ + JSONObject result = verifyResult(resStr); + + return new ResultData() + .setRequestTime(requestTime) + .setRequestContent(paramsXml) + .setResponseTime(responseTime) + .setPatientId(data.getPatid()) + .setResponseContent(resStr) + .setResult(result); + } + + + /** + * 医保预结算 + * + * @param data : + * @return com.dpkj.common.vo.ResultData + * @author 萧道子 2025/10/25 + */ + private ResultData outpatientBudget(OutpatientBeginModel data) { + /** 1、组装参数 */ + JSONObject params = new JSONObject() + .fluentPut("patid", data.getPatientId()) // 病人ID + .fluentPut("cfxhhj", data.getPrescriptionNo()) // 划价单据,格式:单据1,单据2,单据3 + .fluentPut("czksfbz", "0") // 是否扣院内账户,0不使用院内账户,1使用院内账户(默认不使用院内账户) + .fluentPut("zfjsbz", "0") // 是否自费结算,0根据医保代码缴费,1自费结算(默认自费结算) + .fluentPut("ybrc", ""); // 医保入参,xml节点 + String paramsXml = this.handleParams(params); + + String requestTime = DateUtil.now(); + + /** 2、调用COM函数 */ + log.debug("[HispayServiceImpl][outpatientBudget][门诊缴费-预算] 接口入参:{}", paramsXml); + Variant resVariant = new Variant("", true); + Variant call = Dispatch.call(dispatch, "fRun", "BMZJF001", paramsXml, resVariant); + String resStr = resVariant.getStringRef(); + log.debug("[HispayServiceImpl][outpatientBudget][门诊缴费-预算] call返回值:{} 结果:{}", call, resStr); + + String responseTime = DateUtil.now(); + + /** 3、处理结果 */ + JSONObject result = verifyResult(resStr); + + return new ResultData() + .setRequestTime(requestTime) + .setRequestContent(paramsXml) + .setResponseTime(responseTime) + .setPatientId(data.getPatientId()) + .setResponseContent(resStr) + .setResult(result); + } + /** * 处理参数 * @@ -77,7 +220,7 @@ public class HispayServiceImpl implements IHispayService { * @return java.lang.String * @author 萧道子 2025/5/27 */ - private String processParameters(JSONObject params, String password) { + private String handleParams(JSONObject params, String password) { JSONObject req = new JSONObject() {{ put("timestamp", ""); // 请求发送时间 DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") put("requestid", ""); // 唯一请求id IDGenerator.getSnowflakeIdToStr() @@ -85,8 +228,11 @@ public class HispayServiceImpl implements IHispayService { put("password", password); // 密码 put("params", params); }}; - Document document = XmlUtil.mapToXml(req, "request"); - return XmlUtil.toStr(document, "UTF-8", false, true); + return this.JsonObjectToXml(req); + } + + private String handleParams(JSONObject params) { + return handleParams(params, null); } @@ -98,7 +244,6 @@ public class HispayServiceImpl implements IHispayService { * @author 萧道子 2025/5/28 */ private JSONObject verifyResult(String resStr) { - if (StrUtil.isBlank(resStr)) { throw new RuntimeException("信息获取失败"); } @@ -119,149 +264,15 @@ public class HispayServiceImpl implements IHispayService { } - @Override - public JSONObject readCode() { - /** 1、组装参数 */ - JSONObject val = new JSONObject() - .fluentPut("cardtype", "9") // 1.就诊卡,2.医保卡,5.门诊号,6.患者姓名和电话号码,8.电子健康码/卡,9.医保电子凭证 - .fluentPut("cardno", "") // 患者就诊卡号 - .fluentPut("sfzh", "") // 身份证号 - .fluentPut("hzxm", "") // 患者姓名 - .fluentPut("phone", ""); // 患者电话号码 - String params = processParameters(val, null); - log.debug("[HispayServiceImpl][readCode][医保读卡-电子凭证] 接口入参:{}", params); - - /** 2、调用COM函数 */ - Variant vres = new Variant("", true); - Variant call = Dispatch.call(dispatch, "fRun", "BMZXX010", params, vres); - - String resStr = vres.getStringRef(); - log.debug("[HispayServiceImpl][readCode][医保读卡-电子凭证] call返回值:{} 结果:{}", call, resStr); - - - /** 3、处理读卡结果 */ - JSONObject result = verifyResult(resStr); - if (!result.containsKey("item")) { - throw new RuntimeException("item数据为空"); - } - - return result.getJSONObject("item"); + /** + * 将jsonobject转为xml + * + * @param val : + * @return java.lang.String + * @author 萧道子 2025/10/25 + */ + private String JsonObjectToXml(JSONObject val) { + return XmlUtil.toStr(XmlUtil.mapToXml(val, "request"), "UTF-8", false, true); } - @Override - public JSONObject readCard(String password) { - - /** 1、组装参数 */ - JSONObject val = new JSONObject() - .fluentPut("cardtype", "2") // 1.就诊卡,2.医保卡,5.门诊号,6.患者姓名和电话号码,8.电子健康码/卡,9.医保电子凭证 - .fluentPut("cardno", "") // 患者就诊卡号 - .fluentPut("sfzh", "") // 身份证号 - .fluentPut("hzxm", "") // 患者姓名 - .fluentPut("phone", ""); // 患者电话号码 - String params = processParameters(val, password); - log.debug("[HispayServiceImpl][readCard][医保读卡-医保卡] 接口入参:{}", params); - - /** 2、调用COM函数 */ - Variant vres = new Variant("", true); - Variant call = Dispatch.call(dispatch, "fRun", "BMZXX010", params, vres); - - String resStr = vres.getStringRef(); - log.debug("[HispayServiceImpl][readCard][医保读卡-医保卡] call返回值:{} 结果:{}", call, resStr); - - /** 3、处理读卡结果 */ - JSONObject result = verifyResult(resStr); - if (!result.containsKey("item")) { - throw new RuntimeException("item数据为空"); - } - - return result.getJSONObject("item"); - } - - - private ResultData outpatientBudget(OutpatientBeginModel data) { - - /** 1、组装参数 */ - JSONObject val = new JSONObject() - .fluentPut("patid", data.getPatientId()) // 病人ID - .fluentPut("cfxhhj", data.getPrescriptionNo()) // 划价单据,格式:单据1,单据2,单据3 - .fluentPut("czksfbz", "0") // 是否扣院内账户,0不使用院内账户,1使用院内账户(默认不使用院内账户) - .fluentPut("zfjsbz", "0") // 是否自费结算,0根据医保代码缴费,1自费结算(默认自费结算) - .fluentPut("ybrc", ""); // 医保入参,xml节点 - String params = processParameters(val, data.getPassword()); - log.debug("[HispayServiceImpl][outpatientBudget][门诊缴费-预算] 接口入参:{}", params); - - String requestTime = DateUtil.now(); - - /** 2、调用COM函数 */ - Variant resVariant = new Variant("", true); - Variant call = Dispatch.call(dispatch, "fRun", "BMZJF001", params, resVariant); - - String responseTime = DateUtil.now(); - String resStr = resVariant.getStringRef(); - log.debug("[HispayServiceImpl][outpatientBudget][门诊缴费-预算] call返回值:{} 结果:{}", call, resStr); - - - /** 3、处理结果 */ - JSONObject result = verifyResult(resStr); - - return new ResultData() - .setRequestTime(requestTime) - .setRequestContent(params) - .setResponseTime(responseTime) - .setPatientId(data.getPatientId()) - .setResponseContent(resStr) - .setResult(result); - } - - - @Override - public ResultData chsCodeAsOutpatientBegin(OutpatientBeginModel data) { - // 用户读卡-生成token - JSONObject userInfo = readCode(); - String patientId = userInfo.getString("patid"); - data.setPatientId(patientId); - - // 更新常量状态 已读卡 - ChsPayStateConst.put(data.getPrescriptionNo(), 1); - - return outpatientBudget(data); - } - - - @Override - public ResultData chsCodeAsOutpatientFinal(OutpatientFinalModel data) { - - /** 1、组装参数 */ - String requestTime = DateUtil.now(); - data.setPaytime(requestTime); - JSONObject val = ((JSONObject) JSON.toJSON(data)) - .fluentPut("czksfbz", "0") // 是否扣院内账户,与预算保持一致 - .fluentPut("zfjsbz", "0") // 是否自费结算,与预算保持一致,0根据病人医保代码结算,1自费结算 - .fluentPut("ybrc", "") - .fluentPut("ptlsh", "") - .fluentPut("jysm", ""); - String params = processParameters(val, null); - log.debug("[HispayServiceImpl][chsCodeAsOutpatientFinal][门诊缴费-结算] 接口入参:{}", params); - - /** 2、调用COM函数 */ - Variant resVariant = new Variant("", true); - Variant call = Dispatch.call(dispatch, "fRun", "BMZJF002", params, resVariant); - - String responseTime = DateUtil.now(); - String resStr = resVariant.getStringRef(); - log.debug("[HispayServiceImpl][chsCodeAsOutpatientFinal][门诊缴费-结算] call返回值:{} 结果:{}", call, resStr); - - /** 3、处理结果 */ - JSONObject result = verifyResult(resStr); - - return new ResultData() - .setRequestTime(requestTime) - .setRequestContent(params) - .setResponseTime(responseTime) - .setPatientId(data.getPatid()) - .setResponseContent(resStr) - .setResult(result); - } - - } diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/constant/YnchsConst.java b/src/main/java/com/dpkj/modules/chs/ynchs/constant/YnchsConst.java new file mode 100644 index 0000000..cbf8834 --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/constant/YnchsConst.java @@ -0,0 +1,38 @@ +package com.dpkj.modules.chs.ynchs.constant; + +public interface YnchsConst { + + String URL_BASE = "/eapdomain/callService"; + + /** + * 人员信息获取 + */ + String URL_1101 = URL_BASE; + + /** + * 身份认证 + */ + String URL_1191 = URL_BASE; + + /** + * 移动支付-费用明细上传 + */ + String URL_6201 = "/org/local/api/hos/uldFeeInfo"; + + /** + * 移动支付-支付下单 + */ + String URL_6202 = "/org/local/api/hos/pay_order"; + + /** + * 移动支付-医保退费 + */ + String URL_6203 = "/org/local/api/hos/refund_Order"; + + /** + * 移动支付-医保订单信息同步 + */ + String URL_6204 = URL_BASE; + + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/controller/YnChsPayController.java b/src/main/java/com/dpkj/modules/chs/ynchs/controller/YnChsPayController.java index 007d0ce..44167c1 100644 --- a/src/main/java/com/dpkj/modules/chs/ynchs/controller/YnChsPayController.java +++ b/src/main/java/com/dpkj/modules/chs/ynchs/controller/YnChsPayController.java @@ -1,8 +1,9 @@ package com.dpkj.modules.chs.ynchs.controller; -import com.alibaba.fastjson.JSONObject; import com.dpkj.common.vo.Result; -import com.dpkj.modules.chs.ynchs.service.IYnChsPayService; +import com.dpkj.modules.chs.padchs.response.ChsCheckResponse; +import com.dpkj.modules.chs.ynchs.service.IYnchspayByDllService; +import com.dpkj.modules.chs.ynchs.service.IYnchspayByWebapiService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; @@ -20,18 +21,21 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/chs/yn") public class YnChsPayController { - private final IYnChsPayService ynChsPayService; + private final IYnchspayByWebapiService ynchspayByWebapiService; + + private final IYnchspayByDllService ynchspayByDllService; + /** - * 获取支付凭证 + * 刷脸获取身份信息 * * @return com.dpkj.common.vo.Result - * @author 萧道子 2025/10/22 + * @author 萧道子 2025/10/24 */ - @PostMapping("getPayToken") - public Result getPayToken() { + @PostMapping("getUserByFace") + public Result getUserByFace() { try { - JSONObject result = ynChsPayService.getPayToken(); + ChsCheckResponse result = ynchspayByDllService.getUserByFace(); return Result.ok("成功", result); } catch (Exception e) { return Result.error(e.getMessage()); diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/dll/YnChsPayDll.java b/src/main/java/com/dpkj/modules/chs/ynchs/dll/YnChsPayDll.java index b4e85cf..914249f 100644 --- a/src/main/java/com/dpkj/modules/chs/ynchs/dll/YnChsPayDll.java +++ b/src/main/java/com/dpkj/modules/chs/ynchs/dll/YnChsPayDll.java @@ -2,7 +2,6 @@ package com.dpkj.modules.chs.ynchs.dll; import com.sun.jna.Library; import com.sun.jna.Native; -import com.sun.jna.Pointer; import lombok.extern.slf4j.Slf4j; /** @@ -21,8 +20,8 @@ public class YnChsPayDll { */ public static EcDll instance() throws DllRegistrationException { try { - // return Native.load("CHSInterfaceYn.dll", EcDll.class); - return Native.load("D:\\Code\\CHS\\CHSInterfaceYn.dll", EcDll.class); + return Native.load("CHSInterfaceYn.dll", EcDll.class); + // return Native.load("D:\\Code\\CHS\\CHSInterfaceYn.dll", EcDll.class); } catch (UnsatisfiedLinkError e) { log.info("[YnChsPayDll][instance][云南省级医保动态库] SDK注册失败:{}", e.getMessage()); throw new DllRegistrationException("Failed to load YnChsPayDll library: ", e); @@ -62,7 +61,7 @@ public class YnChsPayDll { * @return int * @author 萧道子 2025/10/22 */ - int Init(String fixmeDinsCode, String infoSysCode, String infoSysSign, String url, Pointer errMsg); + int Init(String fixmeDinsCode, String infoSysCode, String infoSysSign, String url, Object errMsg); /** @@ -78,7 +77,7 @@ public class YnChsPayDll { * @return int * @author 萧道子 2025/10/22 */ - int BusinessHandle(String fixmeDinsCode, String infoSysCode, String infoSysSign, String inputData, Pointer outputData, Pointer errMsg); + int BusinessHandle(String fixmeDinsCode, String infoSysCode, String infoSysSign, String inputData, Object outputData, Object errMsg); /** @@ -94,7 +93,7 @@ public class YnChsPayDll { * @return int * @author 萧道子 2025/10/22 */ - int BusinessHandleW(String fixmeDinsCode, String infoSysCode, String infoSysSign, String inputData, Pointer outputData, Pointer errMsg); + int BusinessHandleW(String fixmeDinsCode, String infoSysCode, String infoSysSign, String inputData, Object outputData, Object errMsg); } diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnChsPayService.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnChsPayService.java deleted file mode 100644 index f6dabcd..0000000 --- a/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnChsPayService.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.dpkj.modules.chs.ynchs.service; - -import com.alibaba.fastjson.JSONObject; - -/** - * @Auther: 萧道子 - * @Date: 2025/8/7 14:45 - * @Description: - */ -public interface IYnChsPayService { - - /** - * 下单 - * - * @param val : - * @return void - * @author 萧道子 2025/10/22 - */ - JSONObject placeOrderByApi(String val); - - - /** - * 下单 - * - * @param val : - * @return void - * @author 萧道子 2025/10/22 - */ - JSONObject getPayToken(); -} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByDllService.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByDllService.java new file mode 100644 index 0000000..3cd102a --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByDllService.java @@ -0,0 +1,21 @@ +package com.dpkj.modules.chs.ynchs.service; + +import com.dpkj.modules.chs.padchs.response.ChsCheckResponse; + +/** + * @Auther: 萧道子 + * @Date: 2025/8/7 14:45 + * @Description: + */ +public interface IYnchspayByDllService { + + /** + * 通过扫脸获取用户信息 + * + * @return com.dpkj.modules.chs.padchs.response.ChsCheckResponse + * @author 萧道子 2025/10/24 + */ + ChsCheckResponse getUserByFace(); + + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByWebapiService.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByWebapiService.java new file mode 100644 index 0000000..d96c3b9 --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnchspayByWebapiService.java @@ -0,0 +1,12 @@ +package com.dpkj.modules.chs.ynchs.service; + +/** + * @Auther: 萧道子 + * @Date: 2025/8/7 14:45 + * @Description: + */ +public interface IYnchspayByWebapiService { + + + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnChsPayServiceImpl.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnChsPayServiceImpl.java deleted file mode 100644 index 28489f8..0000000 --- a/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnChsPayServiceImpl.java +++ /dev/null @@ -1,185 +0,0 @@ -package com.dpkj.modules.chs.ynchs.service.impl; - -import cn.hutool.core.date.DatePattern; -import cn.hutool.core.date.DateTime; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.RandomUtil; -import cn.hutool.crypto.SecureUtil; -import cn.hutool.http.HttpRequest; -import cn.hutool.http.HttpResponse; -import com.alibaba.fastjson.JSONObject; -import com.alibaba.fastjson.serializer.SerializerFeature; -import com.dpkj.common.config.YnChsConfig; -import com.dpkj.common.constant.CharsetConst; -import com.dpkj.common.utils.IDGenerator; -import com.dpkj.modules.chs.padchs.response.ChsCheckResponse; -import com.dpkj.modules.chs.padchs.service.IPadChsPayService; -import com.dpkj.modules.chs.ynchs.dll.YnChsPayDll; -import com.dpkj.modules.chs.ynchs.service.IYnChsPayService; -import com.sun.jna.Memory; -import com.sun.jna.Pointer; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -import javax.annotation.PostConstruct; - -/** - * @Auther: 萧道子 - * @Date: 2025/8/7 14:46 - * @Description: - */ -@Slf4j -@Service -public class YnChsPayServiceImpl implements IYnChsPayService { - - private YnChsPayDll.EcDll dll; - - @Autowired - private YnChsConfig ynChsConfig; - @Autowired - private IPadChsPayService padChsPayService; - - @PostConstruct - public void postConstruct() { - log.info("[YnChsPayServiceImpl][postConstruct][云南省局医保DLL] 初始化动态链接库"); - try { - dll = YnChsPayDll.instance(); - } catch (YnChsPayDll.DllRegistrationException e) { - log.error("[YnChsPayServiceImpl][postConstruct][云南省局医保DLL] 加载失败:{}", e.getMessage()); - throw new RuntimeException("云南省局医保DLL库加载失败:" + e.getMessage(), e); - } - } - - - @Override - public JSONObject placeOrderByApi(String val) { - String nonce = IDGenerator.getSnowflakeIdToStr(); - DateTime date = DateUtil.date(); - String timestamp = DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN); - String infoSysSign = ynChsConfig.getInfoSysSign(); - String signature = SecureUtil.sha256(timestamp + infoSysSign + nonce); - - String fixmeDinsCode = ynChsConfig.getFixmeDinsCode(); - String cityCode = ynChsConfig.getCityCode(); - String fixmeDinsName = ynChsConfig.getFixmeDinsName(); - String msgid = fixmeDinsCode + timestamp + RandomUtil.randomNumbers(4); - - JSONObject inputData = new JSONObject().fluentPut("data", new JSONObject().fluentPut("mdtrt_cert_type", "02") - .fluentPut("mdtrt_cert_no", "02") - .fluentPut("card_sn", "") - .fluentPut("begntime", "") - .fluentPut("psn_cert_type", "01") - .fluentPut("certno", "530111199701031117") - .fluentPut("psn_name", "薛家俊")); - JSONObject params = new JSONObject() - .fluentPut("infno", "1101") // 交易编号 - .fluentPut("msgid", msgid) // 发送方报文ID 定点医药机构编号(12)+时间(14)+顺序号(4) 时间格式:yyyyMMddHHmmss - .fluentPut("mdtrtarea_admvs", cityCode) // 就医地医保区划 - .fluentPut("insuplc_admdvs", "") // 参保地医保区划 - .fluentPut("recer_sys_code", "yinyitong") // 接收方系统代码 - .fluentPut("dev_no", "") // 设备编号 - .fluentPut("dev_safe_info", "") // 设备安全信息 - .fluentPut("cainfo", "") // 数字签名信息 - .fluentPut("signtype", "") // 签名类型 - .fluentPut("infver", "V1.0") // 接口版本号 - .fluentPut("opter_type", 3) // 经办人类别 1-经办人;2-自助终端;3-移动终端 - .fluentPut("opter", "test") // 经办人 - .fluentPut("opter_name", "test") // 经办人姓名 - .fluentPut("inf_time", DateUtil.format(date, DatePattern.NORM_DATETIME_PATTERN)) // 交易时间 - .fluentPut("fixmedins_code", fixmeDinsCode) // 定点医药机构编号 - .fluentPut("fixmedins_name", fixmeDinsName) // 定点医药机构名称 - .fluentPut("sign_no", nonce) // 交易签到流水号 - .fluentPut("input", inputData); // 交易输入 - - String paramsStr = params.toString(SerializerFeature.WriteNullStringAsEmpty); - log.debug("[LOG9823][placeOrder][云南省局医保-API][获取支付授权] 请求入参: {}", paramsStr); - // 请求 - HttpResponse response = HttpRequest.post(ynChsConfig.getUrl()) - .header("Content-Type", "application/json") - .header("fixmedins_code", fixmeDinsCode) // 医保机构编码 - .header("infosyscode", ynChsConfig.getInfoSysCode()) // 服务商统一社会信用代码 - .header("hsf_nonce", nonce) // 校验码 非重复的随机字符串(十分钟内不能重复) - .header("hsf_timestamp", timestamp) // 当前时间戳(秒) - .header("hsf_signature", signature) // 加密生成的签名 - .body(paramsStr) - .execute(); - try { - String resultBody = response.body(); - log.debug("[LOG9823][placeOrder][云南省局医保-API][获取支付授权] 请求出参: {}", resultBody); - // 释放资源 - response.close(); - - JSONObject result = JSONObject.parseObject(resultBody); - - // TODO 萧道子 2025/10/22 : 判断逻辑 - - return result; - - } catch (Exception e) { - log.error("[LOG3760][placeOrder][云南省局医保-API][获取支付授权] 请求报错 ERR:{}", e.getMessage()); - e.printStackTrace(); - return null; - } - } - - - @Override - public JSONObject getPayToken() { - // 初始化 - this.init(); - - // 调用刷脸认证 - // ChsCheckResponse userInfo = padChsPayService.getInfoByFace(new ChsModel()); - - ChsCheckResponse userInfo = new ChsCheckResponse() - .setIdNo("530111199701031117") - .setUserName("薛家俊") - .setIdType("01") - .setEcToken("530000fec5tp6km4s626a3720a00005186b127") - .setAuthNo("ano3954912217759795201530000"); - - JSONObject params = new JSONObject() - .fluentPut("orgCodg", "") // 机构编码 - .fluentPut("feeType", "") // 费用类型 - .fluentPut("orgId", ynChsConfig.getFixmeDinsCode()) // 电子凭证机构号 - .fluentPut("idType", userInfo.getIdType()) // 证件类别 - .fluentPut("idNo", userInfo.getIdNo()) // 证件号码 - .fluentPut("userName", userInfo.getUserName()) // 用户姓名 - .fluentPut("ecToken", userInfo.getEcToken()) // 电子凭证授权 ecToken - .fluentPut("authNo", userInfo.getAuthNo()); // 实人认证流水号 - JSONObject inputData = new JSONObject().fluentPut("input", new JSONObject().fluentPut("data", params)); - String inputDataStr = inputData.toString(SerializerFeature.WriteNullStringAsEmpty); - - Pointer errMsg = new Memory(1024 * 10); - Pointer outputData = new Memory(1024 * 10); - - log.debug("[LOG1591][placeOrderByDll][云南省局医保DLL][BusinessHandle][终端获取支付令牌] 请求入参: {}", inputDataStr); - int state = dll.BusinessHandle(ynChsConfig.getFixmeDinsCode(), ynChsConfig.getInfoSysCode(), ynChsConfig.getInfoSysSign(), inputDataStr, outputData, errMsg); - String errMsgStr = errMsg.getString(0, CharsetConst.GB2312); - String outputDataStr = outputData.getString(0, CharsetConst.GB2312); - log.debug("[LOG1591][placeOrderByDll][云南省局医保DLL][BusinessHandle][终端获取支付令牌] 请求出参 data:{} errMsg:{}", outputDataStr, errMsgStr); - - if (state != 0) { - throw new RuntimeException("终端获取支付令牌失败:" + errMsgStr); - } - - JSONObject result = JSONObject.parseObject(outputDataStr); - - return result; - } - - - private void init() { - log.debug("[LOG4945][initPrinter][云南省局医保DLL][Init] 开始"); - Pointer errMsg = new Memory(1024 * 10); - int state = dll.Init(ynChsConfig.getFixmeDinsCode(), ynChsConfig.getInfoSysCode(), ynChsConfig.getInfoSysSign(), ynChsConfig.getUrl(), errMsg); - String errMsgStr = errMsg.getString(0, CharsetConst.GB2312); - log.debug("[LOG4945][initPrinter][云南省局医保DLL][Init] 结束 state:{} errMsg:{}", state, errMsgStr); - - if (state != 0) { - throw new RuntimeException("初始化医保动态库失败:" + errMsgStr); - } - } - -} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByDllServiceImpl.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByDllServiceImpl.java new file mode 100644 index 0000000..81fd9c9 --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByDllServiceImpl.java @@ -0,0 +1,234 @@ +package com.dpkj.modules.chs.ynchs.service.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.dpkj.common.config.PadChsConfig; +import com.dpkj.common.config.YnChsConfig; +import com.dpkj.common.constant.CharsetConst; +import com.dpkj.common.utils.IDGenerator; +import com.dpkj.modules.chs.padchs.constant.ChsDictEnum; +import com.dpkj.modules.chs.padchs.constant.NationECCodeConst; +import com.dpkj.modules.chs.padchs.response.ChsCheckResponse; +import com.dpkj.modules.chs.padchs.service.IPadChsPayService; +import com.dpkj.modules.chs.ynchs.constant.YnchsConst; +import com.dpkj.modules.chs.ynchs.dll.YnChsPayDll; +import com.dpkj.modules.chs.ynchs.service.IYnchspayByDllService; +import com.sun.jna.Memory; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +/** + * @Auther: 萧道子 + * @Date: 2025/8/7 14:46 + * @Description: + */ +@Slf4j +@Service +public class YnchspayByDllServiceImpl implements IYnchspayByDllService { + + // 缓冲区大小 1M + private final long bufferSize = 1024 * 1024; + + private YnChsPayDll.EcDll dll; + + @Autowired + private YnChsConfig ynChsConfig; + @Autowired + private PadChsConfig chsPadConfig; + @Autowired + private IPadChsPayService padChsPayService; + + + @PostConstruct + public void postConstruct() { + log.info("[YnchspayByWebapiServiceImpl][postConstruct][云南省局医保DLL] 初始化动态链接库"); + try { + dll = YnChsPayDll.instance(); + } catch (YnChsPayDll.DllRegistrationException e) { + log.error("[YnchspayByWebapiServiceImpl][postConstruct][云南省局医保DLL] 加载失败:{}", e.getMessage()); + throw new RuntimeException("云南省局医保DLL库加载失败:" + e.getMessage(), e); + } + } + + + @Override + public ChsCheckResponse getUserByFace() { + JSONObject ecinfo = this.chsApi1191(); + return new ChsCheckResponse() + .setAuthNo(ecinfo.getString("authno")) + .setEcToken(ecinfo.getString("ectoken")) + .setEcIndexNo(ecinfo.getString("ecIndexno")) + .setEmail(ecinfo.getString("email")) + .setUserName(ecinfo.getString("username")) + .setIdNo(ecinfo.getString("idno")) + .setIdType(ecinfo.getString("psn_cert_type")) + .setInsuOrg(ecinfo.getString("insuorg")) + .setBirthday(ecinfo.getString("birthday")) + .setGender(ecinfo.getString("gender")) + .setNationality(ecinfo.getString("nationality")); + } + + + /** + * 身份认证 + * + * @return com.alibaba.fastjson.JSONObject + * @author 萧道子 2025/10/24 + */ + public JSONObject chsApi1191() { + JSONObject inputData = new JSONObject() + .fluentPut("mdtrt_cert_type", "01") // 就诊凭证类型 + .fluentPut("cardtype", "") // 卡片类型 + .fluentPut("businesstype", ChsDictEnum.BusinessType.YY_301.str()) // 用码业务类型 + .fluentPut("operatorid", chsPadConfig.getOperatorId()) // 收款员编号 + .fluentPut("operatorname", chsPadConfig.getOperatorName()) // 收款员姓名 + .fluentPut("officeid", chsPadConfig.getOfficeId()) // 医保科室编号 + .fluentPut("officename", chsPadConfig.getOfficeName()) // 科室名称 + .fluentPut("transType", NationECCodeConst.TRANSTYPE_ALICHS_AUTH) // 交易类型 刷脸 + .fluentPut("deviceType", "SelfService") // 设备类型 + .fluentPut("outBizNo", IDGenerator.getSnowflakeIdToStr()) // 定点医药机构本次业务流水号 + .fluentPut("extData", ""); // 扩展参数 + JSONObject result = this.sendDll(YnchsConst.URL_1191, "1191", inputData); + JSONObject ecinfo = result.getJSONObject("ecinfo"); + return ecinfo; + } + + /** + * 人员信息获取 + * + * @param val : + * @return com.alibaba.fastjson.JSONObject + * @author 萧道子 2025/10/23 + */ + public JSONObject chsApi1101(ChsCheckResponse val) { + JSONObject inputData = new JSONObject() + .fluentPut("mdtrt_cert_type", "01") // 就诊凭证类型 + .fluentPut("mdtrt_cert_no", val.getEcToken()) // 就诊凭证编号-电子凭证令牌 + .fluentPut("card_sn", "") // 卡识别码 + .fluentPut("begntime", "") // 开始时间-获取历史参保信息时传入 + .fluentPut("psn_cert_type", val.getIdType()) // 人员证件类型-01代表身份证 + .fluentPut("certno", val.getIdNo()) // 证件号码 + .fluentPut("psn_name", val.getUserName()); // 人员姓名 + JSONObject result = this.sendDll(YnchsConst.URL_1101, "1101", inputData); + return result; + } + + /** + * 初始化函数 + * + * @param apiUrl : + * @return void + * @author 萧道子 2025/10/24 + */ + private void init(String apiUrl) { + log.debug("[LOG4945][initPrinter][云南省局医保DLL][Init] 开始"); + String url = ynChsConfig.getUrl() + apiUrl; + Memory errPointer = new Memory(bufferSize); + int state = dll.Init(ynChsConfig.getFixmeDinsCode(), ynChsConfig.getInfoSysCode(), ynChsConfig.getInfoSysSign(), url, errPointer); + String errValueStr = errPointer.getString(0, CharsetConst.GB2312); + errPointer.close(); + log.debug("[LOG4945][initPrinter][云南省局医保DLL][Init] 结束 state:{} errMsg:{}", state, errValueStr); + if (state != 0) { + throw new RuntimeException("初始化医保动态库失败:" + errValueStr); + } + } + + /** + * 发起请求 + * + * @param url : 请求地址 + * @param infno : 交易编码 + * @param inputData : 请求值 + * @return com.alibaba.fastjson.JSONObject + * @author 萧道子 2025/10/23 + */ + private JSONObject sendDll(String url, String infno, JSONObject inputData) { + // 一、初始化 + this.init(url); + + // 二、设置公共请求参数 + String infoSysSign = ynChsConfig.getInfoSysSign(); + String fixmeDinsCode = ynChsConfig.getFixmeDinsCode(); + String infoSysCode = ynChsConfig.getInfoSysCode(); + String cityCode = ynChsConfig.getCityCode(); + String fixmeDinsName = ynChsConfig.getFixmeDinsName(); + + String nonce = IDGenerator.getSnowflakeIdToStr(); + DateTime date = DateUtil.date(); + String timestamp = DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN); + + String msgid = fixmeDinsCode + timestamp + RandomUtil.randomNumbers(4); + + // 请求体参数 + JSONObject params = new JSONObject() + .fluentPut("infno", infno) // 交易编号 + .fluentPut("msgid", msgid) // 发送方报文ID 定点医药机构编号(12)+时间(14)+顺序号(4) 时间格式:yyyyMMddHHmmss + .fluentPut("mdtrtarea_admvs", cityCode) // 就医地医保区划 + .fluentPut("insuplc_admdvs", "") // 参保地医保区划 + .fluentPut("recer_sys_code", "yinyitong") // 接收方系统代码 + .fluentPut("dev_no", "") // 设备编号 + .fluentPut("dev_safe_info", "") // 设备安全信息 + .fluentPut("cainfo", "") // 数字签名信息 + .fluentPut("signtype", "") // 签名类型 + .fluentPut("infver", "V1.0") // 接口版本号 + .fluentPut("opter_type", "2") // 经办人类别 1-经办人;2-自助终端;3-移动终端 + .fluentPut("opter", "test") // 经办人 + .fluentPut("opter_name", "test") // 经办人姓名 + .fluentPut("inf_time", DateUtil.format(date, DatePattern.NORM_DATETIME_PATTERN)) // 交易时间 + .fluentPut("fixmedins_code", fixmeDinsCode) // 定点医药机构编号 + .fluentPut("fixmedins_name", fixmeDinsName) // 定点医药机构名称 + .fluentPut("sign_no", nonce) // 交易签到流水号 + .fluentPut("input", new JSONObject().fluentPut("data", inputData)); // 交易输入 + String paramsStr = params.toString(SerializerFeature.WriteNullStringAsEmpty); + + // 三、发起请求 + log.debug("[LOG1591][sendHttp][云南省局医保DLL][{}] 请求入参: {}", infno, paramsStr); + + Memory errPointer = new Memory(bufferSize); + Memory outPointer = new Memory(bufferSize); + int state = dll.BusinessHandle(fixmeDinsCode, infoSysCode, infoSysSign, paramsStr, outPointer, errPointer); + String errValueStr = errPointer.getString(0, CharsetConst.GB2312); + String outValueStr = outPointer.getString(0, CharsetConst.GB2312); + errPointer.close(); + outPointer.close(); + + log.debug("[LOG1591][sendHttp][云南省局医保DLL][{}] 请求出参 state:{} errMsg:{} data:{} ", infno, state, errValueStr, outValueStr); + + // 四、处理响应 + if (state != 0) { + log.error("[LOG8761][sendDll][云南省局医保DLL][{}] 医保通用接口BusinessHandle调用失败 state:{}", infno, state); + throw new RuntimeException("医保接口请求失败!"); + } + + if (StrUtil.isEmpty(outValueStr)) { + log.error("[LOG8762][sendDll][云南省局医保DLL][{}] 医保接口响应数据为空 state:{} ", infno, state); + throw new RuntimeException("医保接口响应数据为空:" + errValueStr); + } + + JSONObject result; + try { + result = JSONObject.parseObject(outValueStr); + } catch (Exception e) { + log.error("[LOG4410][sendDll][云南省局医保DLL][{}] 医保响应数转JSON失败 ERR:{}", infno, e.getMessage()); + throw new RuntimeException("医保响应数据解析失败!"); + } + + if (result.getInteger("infcode") != 0) { + String errmsg = result.getString("err_msg"); + log.error("[LOG4411][sendDll][云南省局医保DLL][{}] 医保方系统错误 ERR:{}", infno, errmsg); + throw new RuntimeException("医保请求失败:" + errmsg); + } + + JSONObject output = result.getJSONObject("output"); + return output; + } + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByWebapiServiceImpl.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByWebapiServiceImpl.java new file mode 100644 index 0000000..6727ebc --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnchspayByWebapiServiceImpl.java @@ -0,0 +1,233 @@ +package com.dpkj.modules.chs.ynchs.service.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.dpkj.common.config.YnChsConfig; +import com.dpkj.common.utils.IDGenerator; +import com.dpkj.modules.chs.padchs.response.ChsCheckResponse; +import com.dpkj.modules.chs.padchs.service.IPadChsPayService; +import com.dpkj.modules.chs.ynchs.constant.YnchsConst; +import com.dpkj.modules.chs.ynchs.service.IYnchspayByWebapiService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +/** + * @Auther: 萧道子 + * @Date: 2025/8/7 14:46 + * @Description: + */ +@Slf4j +@Service +public class YnchspayByWebapiServiceImpl implements IYnchspayByWebapiService { + @Autowired + private YnChsConfig ynChsConfig; + @Autowired + private IPadChsPayService padChsPayService; + + @PostConstruct + public void postConstruct() { + + } + + + /** + * 人员信息获取 + * + * @param val : + * @return com.alibaba.fastjson.JSONObject + * @author 萧道子 2025/10/23 + */ + private JSONObject chsApi1101(ChsCheckResponse val) { + JSONObject inputData = new JSONObject() + .fluentPut("mdtrt_cert_type", "01") // 就诊凭证类型 + .fluentPut("mdtrt_cert_no", val.getEcToken()) // 就诊凭证编号-电子凭证令牌 + .fluentPut("card_sn", "") // 卡识别码 + .fluentPut("begntime", "") // 开始时间-获取历史参保信息时传入 + .fluentPut("psn_cert_type", val.getIdType()) // 人员证件类型-01代表身份证 + .fluentPut("certno", val.getIdNo()) // 证件号码 + .fluentPut("psn_name", val.getUserName()); // 人员姓名 + + JSONObject result = this.sendHttp(YnchsConst.URL_1101, "1101", inputData); + return result; + } + + + /** + * 订单支付-费用明细上传 + * + * @return com.alibaba.fastjson.JSONObject + * @author 萧道子 2025/10/23 + */ + private JSONObject chsApi6201(JSONObject val) { + String fixmeDinsCode = ynChsConfig.getFixmeDinsCode(); + JSONObject params = new JSONObject() + .fluentPut("orgCodg", fixmeDinsCode) // 机构编码-医保分配 字符型40-Y + .fluentPut("orgId", "") // 电子凭证机构号-电子凭证中台分配,待机构电子凭证建设完毕后可获取该机构号 字符型40 + .fluentPut("psnNo", "") // 人员编号 字符型30-Y + .fluentPut("insutype", "") // 险种类型-字典险种类型 (insutype) 字符型6-Y + .fluentPut("medOrgOrd", "") // 医疗机构订单号-院内产生惟一流水,可关联到一次结算记录,结算成功回调入参返回 字符型40-Y + .fluentPut("initRxOrd", "") // 要续方的原处方流水-rxCircFlag 为1时必传,续方时必传 字符型40 + .fluentPut("rxCircFlag", "") // 电子处方流转标志-1:电子处方 ,0不是电子处方,默认0 字符型1 + .fluentPut("begntime", "") // 开始时间-挂号时间yyyy-MM-dd HH:mm:ss 字符型19-Y + .fluentPut("idNo", "") // 证件号码 字符型40-Y + .fluentPut("userName", "") // 用户姓名 字符型40-Y + .fluentPut("idType", "") // 证件类别-字典人员证件类型(psn_cert_type) 字符型3-Y + .fluentPut("ecToken", "") // 电子凭证授权ecToken-电子凭证解码返回 字符型64-Y + .fluentPut("insuCode", "") // 就诊参保地行政区划-同核心FSI接口入参mdtrtarea_admvs 字符型6-Y + .fluentPut("iptOtpNo", "") // 住院/门诊号-院内唯一流水 字符型30-Y + .fluentPut("atddrNo", "") // 医师编码 字符型30 + .fluentPut("drName", "") // 医师姓名 字符型50 + .fluentPut("deptCode", "") // 科室编码 字符型30 + .fluentPut("deptName", "") // 科室名称 字符型100-Y + .fluentPut("caty", "") // 科别 字符型6-Y + .fluentPut("mdtrtId", "") // 就诊ID 字符型30 + .fluentPut("medType", "") // 医疗类别-字典医疗类别(med_type) 字符型6-Y + .fluentPut("feeType", "") // 费用类型-字典费用类型(feeType) 字符型3-Y + .fluentPut("medfeeSumamt", "") // 医疗费总额 数值型16,2-Y + .fluentPut("acctUsedFlag", "") // 个人账户使用标志-药店上传费用时必填 字符型1-Y + .fluentPut("mainCondDscr", "") // 主要病情描述 字符型1000 + .fluentPut("diseCodg", "") // 病种编码-按照标准编码填写:按病种结算病种目录代码(bydise_setl_list_code)、门诊慢特病病种目录代码(opsp_dise_cod) 字符型30-Y + .fluentPut("diseName", "") // 病种名称 字符型500-Y + .fluentPut("psnSetlway", "") // 个人结算方式 字符型6-Y + .fluentPut("chrgBchno", "") // 收费批次号 字符型30-Y + .fluentPut("pubHospRfomFlag", "") // 公立医院改革标志-可参考FSI的接口要求 字符型6-Y + .fluentPut("invono", "") // 发票号 字符型20 + .fluentPut("endtime", "") // 出院时间-yyyy-MM-dd HH:mm:ss 字符型19 + .fluentPut("fulamtOwnpayAmt", "") // 全自费金额-住院结算时需要 数值型16,2 + .fluentPut("overlmtSelfpay", "") // 超限价金额-住院结算时需要 数值型16,2 + .fluentPut("preselfpayAmt", "") // 先行自付金额-住院结算时需要 数值型16,2 + .fluentPut("inscpScpAmt", "") // 符合政策范围金额-住院结算时需要 数值型16,2 + .fluentPut("oprnOprtCode", "") // 手术操作代码-住院结算时需要 字符型30 + .fluentPut("oprnOprtName", "") // 手术操作名称-住院结算时需要 字符型500 + .fluentPut("fpscNo", "") // 计划生育服务证号-住院结算时需要 字符型50 + .fluentPut("latechbFlag", "") // 晚育标志-住院结算时需要 字符型3 + .fluentPut("gesoVal", "") // 孕周数-住院结算时需要 数值型2 + .fluentPut("fetts", "") // 胎次-住院结算时需要 数值型3-Y + .fluentPut("fetusCnt", "") // 胎儿数-住院结算时需要 数值型3 + .fluentPut("pretFlag", "") // 早产标志-住院结算时需要 字符型3 + .fluentPut("birctrlType", "") // 计划生育手术类别-生育门诊按需录入 字符型6 + .fluentPut("birctrlMatnDate", "") // 计划生育手术或生育日期-生育门诊按需录入,yyyy-MM-dd 字符型10 + .fluentPut("copFlag", "") // 伴有并发症标志-住院结算时需要 字符型3 + .fluentPut("dscgDeptCodg", "") // 出院科室编码-住院结算时需要 字符型30 + .fluentPut("dscgDeptName", "") // 出院科室名称-住院结算时需要 字符型100 + .fluentPut("dscgDed", "") // 出院床位-住院结算时需要 字符型50 + .fluentPut("dscgWay", "") // 离院方式-住院结算时需要 字符型8 + .fluentPut("dieDate", "") // 死亡日期-yyyy-MM-dd 字符型10 + .fluentPut("matnType", "") // 生育类别-住院结算时需要 字符型6-Y + .fluentPut("expContent", "") // 扩展参数-可参考FSI的接口要求 字符型4000 + .fluentPut("midSetlFlag", "") // 中途结算标志-字典中途结算标志(mid_setl_flag) 字符型3 + .fluentPut("diseinfoList", "") // 诊断或症状明细-Y-见下方diseinfoList定义 + .fluentPut("feedetailList", "") // 费用明细-Y-见下方feedetailList定义 + .fluentPut("admDiagDscr", "") // 入院诊断描述-住院结算时需要 字符型200 + .fluentPut("admDeptCodg", "") // 入院科室编码-住院结算时需要 字符型30 + .fluentPut("admDeptName", "") // 入院科室名称-住院结算时需要 字符型100 + .fluentPut("admBed", "") // 入院床位-住院结算时需要 字符型30 + .fluentPut("payAuthNo", "") // 支付授权码-线上授权返回 字符型40 + .fluentPut("uldLatlnt", "") // 经纬度-格式:经度,纬度如:118.096435,24.485407定位失败情况下传:0,0 字符型21 + .fluentPut("mdtrtCertType", "") // 就诊凭证类型-“00” 无就诊介质,“01” 电子凭证,“02”居民身份证,“03”社会保障卡 字符型2 + .fluentPut("insuplcAdmdvs", "") // 用户参保地行政区划-同核心FSI参数insuplc_admdvs 字符型6-Y + .fluentPut("dscgFlag", "") // 出院登记标志-住院结算时需要,0:由中台调用出院登记1:由定点调用出院登记 字符型1 + .fluentPut("opterType", "") // 操作员类别-为空时使用移动支付中心默认 字符型10 + .fluentPut("devSafeInfo", "") // 设备安全编码-为空时使用移动支付中心默默认 字符型30 + .fluentPut("setlType", "") // 结算类别-OWN:自费类别放空默认为医保订单 字符型30 + .fluentPut("fixmedinsMdtrtId", "") // 医药机构就诊ID-setlType为OWN且当地要求自费数据要上传核心时必传,参考FSI要求 字符型30 + .fluentPut("ownpayType", "") // 自费类型-setlType为OWN且当地要求自费数据要上传核心时传可按需传,参考FSI要求 字符型3 + .fluentPut("elecBillnoCode", "") // 电子票据号码-setlType为OWN且当地要求自费数据要上传核心时传可按需传,参考FSI要求。如果是纸质发票则填写纸质发票号码 字符型50 + .fluentPut("elecBillCode", "") // 电子票据代码-setlType为OWN且当地要求自费数据要上传核心时传可按需传,参考FSI要求 字符型50 + .fluentPut("elecBillChkcode", "") // 电子票据校验码-setlType为OWN且当地要求自费数据要上传核心时传可按需传,参考FSI要求 字符型6 + .fluentPut("onlineDrugChnlAppId", "") // 线上购药渠道id-线上购药业务时,必传,见移动支付反馈单 字符型40 + .fluentPut("drugCrtfCodg", "") // 线上购药渠道认证编码-线上购药业务时,必传,见移动支付线上购药反馈单 字符型40 + ; + + JSONObject result = sendHttp(YnchsConst.URL_6201, "6201", params); + + return null; + } + + + /** + * 发起请求 + * + * @param url : + * @param infno : + * @param inputData : + * @return com.alibaba.fastjson.JSONObject + * @author 萧道子 2025/10/23 + */ + private JSONObject sendHttp(String url, String infno, JSONObject inputData) { + + // 请求头参数 + String infoSysSign = ynChsConfig.getInfoSysSign(); + String fixmeDinsCode = ynChsConfig.getFixmeDinsCode(); + String infoSysCode = ynChsConfig.getInfoSysCode(); + String baseUrl = ynChsConfig.getUrl(); + String cityCode = ynChsConfig.getCityCode(); + String fixmeDinsName = ynChsConfig.getFixmeDinsName(); + + String nonce = IDGenerator.getSnowflakeIdToStr(); + DateTime date = DateUtil.date(); + String timestamp = DateUtil.format(date, DatePattern.PURE_DATETIME_PATTERN); + String signature = SecureUtil.sha256(timestamp + infoSysSign + nonce); + + String msgid = fixmeDinsCode + timestamp + RandomUtil.randomNumbers(4); + + // 请求体参数 + JSONObject params = new JSONObject() + .fluentPut("infno", infno) // 交易编号 + .fluentPut("msgid", msgid) // 发送方报文ID 定点医药机构编号(12)+时间(14)+顺序号(4) 时间格式:yyyyMMddHHmmss + .fluentPut("mdtrtarea_admvs", cityCode) // 就医地医保区划 + .fluentPut("insuplc_admdvs", "") // 参保地医保区划 + .fluentPut("recer_sys_code", "yinyitong") // 接收方系统代码 + .fluentPut("dev_no", "") // 设备编号 + .fluentPut("dev_safe_info", "") // 设备安全信息 + .fluentPut("cainfo", "") // 数字签名信息 + .fluentPut("signtype", "") // 签名类型 + .fluentPut("infver", "V1.0") // 接口版本号 + .fluentPut("opter_type", 3) // 经办人类别 1-经办人;2-自助终端;3-移动终端 + .fluentPut("opter", "test") // 经办人 + .fluentPut("opter_name", "test") // 经办人姓名 + .fluentPut("inf_time", DateUtil.format(date, DatePattern.NORM_DATETIME_PATTERN)) // 交易时间 + .fluentPut("fixmedins_code", fixmeDinsCode) // 定点医药机构编号 + .fluentPut("fixmedins_name", fixmeDinsName) // 定点医药机构名称 + .fluentPut("sign_no", nonce) // 交易签到流水号 + .fluentPut("input", new JSONObject().fluentPut("data", inputData)); // 交易输入 + String paramsStr = params.toString(SerializerFeature.WriteNullStringAsEmpty); + + try { + // 请求 + log.debug("[LOG7900][sendHttp][云南省局医保-API][{}] 请求入参: {}", infno, paramsStr); + HttpResponse response = HttpRequest.post(baseUrl + url) + .header("Content-Type", "application/json") + .header("fixmedins_code", fixmeDinsCode) // 医保机构编码 + .header("infosyscode", infoSysCode) // 服务商统一社会信用代码 + .header("hsf_nonce", nonce) // 校验码 非重复的随机字符串(十分钟内不能重复) + .header("hsf_timestamp", timestamp) // 当前时间戳(秒) + .header("hsf_signature", signature) // 加密生成的签名 + .body(paramsStr) + .execute(); + String resultBody = response.body(); + log.debug("[LOG7900][sendHttp][云南省局医保-API][{}] 请求出参: {}", infno, resultBody); + // 释放资源 + response.close(); + + // 转换参数 + JSONObject result = JSONObject.parseObject(resultBody); + return result; + } catch (Exception e) { + // e.printStackTrace(); + log.error("[LOG3010][sendHttp][云南省局医保-API][{}] 请求失败 ERR:{}", infno, e.getMessage()); + throw new RuntimeException("医保接口请求失败!"); + } + } + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/utils/BCUtils.java b/src/main/java/com/dpkj/modules/chs/ynchs/utils/BCUtils.java new file mode 100644 index 0000000..919f2a2 --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/utils/BCUtils.java @@ -0,0 +1,39 @@ +package com.dpkj.modules.chs.ynchs.utils; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import java.security.*; + +public class BCUtils { + + static { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)==null){ + Security.addProvider(new BouncyCastleProvider()); + } + } + + public static Cipher getCipher(final String algorithm) { + try { + return Cipher.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); + } catch (final NoSuchAlgorithmException | NoSuchPaddingException | NoSuchProviderException e) { + throw new IllegalArgumentException(e); + } + } + + public static MessageDigest getMessageDigest(final String algorithm) { + try { + return MessageDigest.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); + } catch (final NoSuchAlgorithmException|NoSuchProviderException e) { + throw new IllegalArgumentException(e); + } + } + public static Signature getSignature(final String algorithm) { + try { + return Signature.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); + } catch (final NoSuchAlgorithmException|NoSuchProviderException e) { + throw new IllegalArgumentException(e); + } + } +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/utils/EasyGmUtils.java b/src/main/java/com/dpkj/modules/chs/ynchs/utils/EasyGmUtils.java new file mode 100644 index 0000000..c93996f --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/utils/EasyGmUtils.java @@ -0,0 +1,361 @@ +package com.dpkj.modules.chs.ynchs.utils; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.gm.GMNamedCurves; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey; +import org.bouncycastle.jcajce.spec.SM2ParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; + +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; +import java.io.IOException; +import java.math.BigInteger; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Signature; +import java.util.Arrays; +import java.util.Base64; + +/** + * @Author: fuchengmu + * @Date: 2019-10-05 14:58 + *

+ * 用BC的注意点: + * 这个版本的BC对SM3withSM2的结果为asn1格式的r和s,如果需要直接拼接的r||s需要自己转换。下面rsAsn1ToPlainByteArray、rsPlainByteArrayToAsn1就在干这事。 + * 这个版本的BC对SM2的结果为C1||C2||C3,据说为旧标准,新标准为C1||C3||C2,用新标准的需要自己转换。下面changeC1C2C3ToC1C3C2、changeC1C3C2ToC1C2C3就在干这事。 + */ +public class EasyGmUtils { + private static X9ECParameters x9ECParameters = GMNamedCurves.getByName("sm2p256v1"); + private static ECDomainParameters ecDomainParameters = new ECDomainParameters(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN()); + private static ECParameterSpec ecParameterSpec = new ECParameterSpec(x9ECParameters.getCurve(), x9ECParameters.getG(), x9ECParameters.getN()); + + + public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, byte[] privateKeyBytes) { + BCECPrivateKey bcecPrivateKey = getPrivatekeyFromD(BigIntegers.fromUnsignedByteArray(privateKeyBytes)); + return signSm3WithSm2(msg, userId, bcecPrivateKey); + + } + + /** + * @param msg + * @param userId + * @param privateKey + * @return r||s,直接拼接byte数组的rs + */ + public static byte[] signSm3WithSm2(byte[] msg, byte[] userId, PrivateKey privateKey) { + return rsAsn1ToPlainByteArray(signSm3WithSm2Asn1Rs(msg, userId, privateKey)); + } + + + /** + * @param msg + * @param userId + * @param privateKey + * @return rs in asn1 format + */ + public static byte[] signSm3WithSm2Asn1Rs(byte[] msg, byte[] userId, PrivateKey privateKey) { + try { + SM2ParameterSpec parameterSpec = new SM2ParameterSpec(userId); + Signature signer = BCUtils.getSignature("SM3withSM2"); +// signer.setParameter(parameterSpec); + signer.initSign(privateKey, new SecureRandom()); + signer.update(msg, 0, msg.length); + byte[] sig = signer.sign(); + return sig; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static boolean verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, byte[] publicKeyBytes) { + if (publicKeyBytes.length != 64 && publicKeyBytes.length != 65) { + throw new RuntimeException("err key length"); + } + + BigInteger x, y; + if (publicKeyBytes.length > 64) { + x = BigIntegers.fromUnsignedByteArray(publicKeyBytes, 1, 32); + y = BigIntegers.fromUnsignedByteArray(publicKeyBytes, 33, 32); + } else { + x = BigIntegers.fromUnsignedByteArray(publicKeyBytes, 0, 32); + y = BigIntegers.fromUnsignedByteArray(publicKeyBytes, 32, 32); + } + BCECPublicKey bcecPublicKey = getPublickeyFromXY(x, y); + + return verifySm3WithSm2(msg, userId, rs, bcecPublicKey); + } + + /** + * @param msg + * @param userId + * @param rs r||s,直接拼接byte数组的rs + * @param publicKey + * @return + */ + public static boolean verifySm3WithSm2(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey) { + return verifySm3WithSm2Asn1Rs(msg, userId, rsPlainByteArrayToAsn1(rs), publicKey); + } + + /** + * @param msg + * @param userId + * @param rs in asn1 format + * @param publicKey + * @return + */ + public static boolean verifySm3WithSm2Asn1Rs(byte[] msg, byte[] userId, byte[] rs, PublicKey publicKey) { + try { + + Signature verifier = BCUtils.getSignature("SM3withSM2"); +// verifier.setParameter(parameterSpec); + verifier.initVerify(publicKey); + verifier.update(msg, 0, msg.length); + return verifier.verify(rs); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * bc加解密使用旧标c1||c2||c3,此方法在加密后调用,将结果转化为c1||c3||c2 + * + * @param c1c2c3 + * @return + */ + private static byte[] changeC1C2C3ToC1C3C2(byte[] c1c2c3) { + final int c1Len = (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1; // sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。 + final int c3Len = 32; // new SM3Digest().getDigestSize(); + byte[] result = new byte[c1c2c3.length]; + System.arraycopy(c1c2c3, 0, result, 0, c1Len); // c1 + System.arraycopy(c1c2c3, c1c2c3.length - c3Len, result, c1Len, c3Len); // c3 + System.arraycopy(c1c2c3, c1Len, result, c1Len + c3Len, c1c2c3.length - c1Len - c3Len); // c2 + return result; + } + + + /** + * bc加解密使用旧标c1||c3||c2,此方法在解密前调用,将密文转化为c1||c2||c3再去解密 + * + * @param c1c3c2 + * @return + */ + private static byte[] changeC1C3C2ToC1C2C3(byte[] c1c3c2) { + final int c1Len = (x9ECParameters.getCurve().getFieldSize() + 7) / 8 * 2 + 1; // sm2p256v1的这个固定65。可看GMNamedCurves、ECCurve代码。 + final int c3Len = 32; // new SM3Digest().getDigestSize(); + byte[] result = new byte[c1c3c2.length]; + System.arraycopy(c1c3c2, 0, result, 0, c1Len); // c1: 0->65 + System.arraycopy(c1c3c2, c1Len + c3Len, result, c1Len, c1c3c2.length - c1Len - c3Len); // c2 + System.arraycopy(c1c3c2, c1Len, result, c1c3c2.length - c3Len, c3Len); // c3 + return result; + } + + private final static int RS_LEN = 32; + + private static byte[] bigIntToFixexLengthBytes(BigInteger rOrS) { + // for sm2p256v1, n is 00fffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123, + // r and s are the result of mod n, so they should be less than n and have length<=32 + byte[] rs = rOrS.toByteArray(); + if (rs.length == RS_LEN) { + return rs; + } else if (rs.length == RS_LEN + 1 && rs[0] == 0) { + return Arrays.copyOfRange(rs, 1, RS_LEN + 1); + } else if (rs.length < RS_LEN) { + byte[] result = new byte[RS_LEN]; + Arrays.fill(result, (byte) 0); + System.arraycopy(rs, 0, result, RS_LEN - rs.length, rs.length); + return result; + } else { + throw new RuntimeException("err rs: " + Hex.toHexString(rs)); + } + } + + /** + * BC的SM3withSM2签名得到的结果的rs是asn1格式的,这个方法转化成直接拼接r||s + * + * @param rsDer rs in asn1 format + * @return sign result in plain byte array + */ + private static byte[] rsAsn1ToPlainByteArray(byte[] rsDer) { + ASN1Sequence seq = ASN1Sequence.getInstance(rsDer); + byte[] r = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue()); + byte[] s = bigIntToFixexLengthBytes(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue()); + byte[] result = new byte[RS_LEN * 2]; + System.arraycopy(r, 0, result, 0, r.length); + System.arraycopy(s, 0, result, RS_LEN, s.length); + return result; + } + + /** + * BC的SM3withSM2验签需要的rs是asn1格式的,这个方法将直接拼接r||s的字节数组转化成asn1格式 + * + * @param sign in plain byte array + * @return rs result in asn1 format + */ + private static byte[] rsPlainByteArrayToAsn1(byte[] sign) { + if (sign.length != RS_LEN * 2) { + throw new RuntimeException("err rs. "); + } + BigInteger r = new BigInteger(1, Arrays.copyOfRange(sign, 0, RS_LEN)); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(sign, RS_LEN, RS_LEN * 2)); + ASN1EncodableVector v = new ASN1EncodableVector(); + v.add(new ASN1Integer(r)); + v.add(new ASN1Integer(s)); + try { + return new DERSequence(v).getEncoded("DER"); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static BCECPrivateKey getPrivatekeyFromD(BigInteger d) { + ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecParameterSpec); + return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION); + } + + public static BCECPublicKey getPublickeyFromXY(BigInteger x, BigInteger y) { + ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(x9ECParameters.getCurve().createPoint(x, y), ecParameterSpec); + return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION); + } + + /** + * c1||c3||c2 + * + * @param data + * @param key + * @return + */ + public static byte[] sm2Decrypt(byte[] data, PrivateKey key) { + return sm2DecryptOld(changeC1C3C2ToC1C2C3(data), key); + } + + /** + * c1||c3||c2 + * + * @param data + * @param key + * @return + */ + + public static byte[] sm2Encrypt(byte[] data, PublicKey key) { + return changeC1C2C3ToC1C3C2(sm2EncryptOld(data, key)); + } + + /** + * c1||c2||c3 + * + * @param data + * @param key + * @return + */ + public static byte[] sm2EncryptOld(byte[] data, PublicKey key) { + BCECPublicKey localECPublicKey = (BCECPublicKey) key; + ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), ecDomainParameters); + SM2Engine sm2Engine = new SM2Engine(); + sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom())); + try { + return sm2Engine.processBlock(data, 0, data.length); + } catch (InvalidCipherTextException e) { + throw new RuntimeException(e); + } + } + + /** + * c1||c2||c3 + * + * @param data + * @param key + * @return + */ + public static byte[] sm2DecryptOld(byte[] data, PrivateKey key) { + BCECPrivateKey localECPrivateKey = (BCECPrivateKey) key; + ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(localECPrivateKey.getD(), ecDomainParameters); + SM2Engine sm2Engine = new SM2Engine(); + sm2Engine.init(false, ecPrivateKeyParameters); + try { + return sm2Engine.processBlock(data, 0, data.length); + } catch (InvalidCipherTextException e) { + throw new RuntimeException(e); + } + } + + public static byte[] sm4Encrypt(byte[] keyBytes, byte[] plain) { + if (keyBytes.length != 16) { + throw new RuntimeException("err key length"); + } +// if (plain.length % 16 != 0) throw new RuntimeException("err data length"); + + try { + Key key = new SecretKeySpec(keyBytes, "SM4"); + + Cipher out = BCUtils.getCipher("SM4/ECB/PKCS7Padding"); + out.init(Cipher.ENCRYPT_MODE, key); + return out.doFinal(plain); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static byte[] sm4Decrypt(byte[] keyBytes, byte[] cipher) { +// if (keyBytes.length != 16) throw new RuntimeException("err key length"); + if (cipher.length % 16 != 0) { + throw new RuntimeException("err data length"); + } + + try { + Key key = new SecretKeySpec(keyBytes, "SM4"); + Cipher in = BCUtils.getCipher("SM4/ECB/PKCS7Padding"); + in.init(Cipher.DECRYPT_MODE, key); + return in.doFinal(cipher); + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static void main(String[] args) { + byte[] msg = "1234567890".getBytes(); + byte[] userId = "D91CEB11EE62219CD91CEB11EE62219C".getBytes(); + byte[] prvkey = Hex.decode("5B015D0B2C0B6BEBB27EA8"); + byte[] pubkey = Hex.decode("409AEFE32E150677FC2372B758178ECBAD3BBED494975596B3CD603C36F9ED7E690A5B052AFDBD86D46288FA9919BA05F3D48F6532EB28DC02D0DAA35689A50C"); + + prvkey = Base64.getDecoder().decode("JShsBOJL0RgPAoPttEB1hgtPAvCikOl0V1oTOYL7k5U="); + pubkey = Base64.getDecoder().decode("BE8d9gaaCGrE4dHErWCjqyahm6X8l6Rd7fOGx0gNyrGPp0XDoPbbpu1pk9A2fZ9rEsBtwB1Aecnto/gH4h+T7cY="); + byte[] asig = signSm3WithSm2(msg, userId, prvkey); + System.out.println(Hex.toHexString(asig)); + boolean verified = verifySm3WithSm2(msg, userId, asig, pubkey); + System.out.println(verified); + + + String plainString = "1234567890abcdef"; + byte[] plain = plainString.getBytes(); + System.out.println(new String(plain)); +// byte[] key = Hex.decode("0123456789abcdeffedcba9876543210"); + byte[] sm4key = "0123456789abcdef".getBytes(); +// byte[] cipher = Hex.decode("595298c7c6fd271f0402f804c33d3f66"); + byte[] bs = sm4Encrypt(sm4key, plain); + System.out.println(Hex.toHexString(bs)); + ; + bs = sm4Decrypt(sm4key, bs); + System.out.println(new String(bs)); + System.out.println(Hex.toHexString(bs)); + } + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/utils/HseEncAndDecUtil.java b/src/main/java/com/dpkj/modules/chs/ynchs/utils/HseEncAndDecUtil.java new file mode 100644 index 0000000..aea6fa1 --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/utils/HseEncAndDecUtil.java @@ -0,0 +1,278 @@ +package com.dpkj.modules.chs.ynchs.utils; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.bouncycastle.util.encoders.Hex; + +import java.util.Base64; +import java.util.Iterator; +import java.util.Map; + +/** + * @author: muweng + * @date: 2020/1/20 21:33 + * @description: 电子凭证加解密和. + */ +public class HseEncAndDecUtil { + + + /** + * sm2签名 + * + * @param message 未加密报文 + * @param sm4key 渠道sm4密钥 + * @param prvKey 渠道私钥 + * @return 签名串 String + * @throws Exception + */ + public static String signature(String message, String sm4key, String prvKey) { + byte[] messageByte; + try { + JSONObject jsonObject = JSON.parseObject(message); + removeEmpty(jsonObject); + messageByte = SignUtil.getSignText(jsonObject, sm4key).getBytes("UTF-8"); + } catch (Exception e) { + messageByte = message.getBytes(); + } + byte[] chnlSecretByte = sm4key.getBytes(); + byte[] prvkey = Base64.getDecoder().decode(prvKey); + return Base64.getEncoder().encodeToString(EasyGmUtils.signSm3WithSm2(messageByte, chnlSecretByte, prvkey)); + } + + /** + * sm2验签 + * + * @param msg sm4解密后报文 + * @param source 原始响应报文 + * @param signData 签名串 + * @param sm4key 渠道密钥 + * @param pubKey 平台公钥 + * @return 验证是否通过 boolean + * @throws Exception + */ + public static boolean verify(String msg, String source, String signData, String sm4key, String pubKey) { + byte[] msgByte; + try { + JSONObject jsonObject = JSON.parseObject(msg); + JSONObject jsonObjects = JSON.parseObject(source); + jsonObjects.remove("signData"); + jsonObjects.remove("encData"); + jsonObjects.put("data", jsonObject); + removeEmpty(jsonObject); + String str = SignUtil.getSignText(jsonObjects, sm4key); + msgByte = str.getBytes("UTF-8"); + } catch (Exception e) { + msgByte = msg.getBytes(); + } + byte[] signatureByte = Base64.getDecoder().decode(signData), + chnlSecretByte = sm4key.getBytes(), + pubKeyByte = Base64.getDecoder().decode(pubKey); + return EasyGmUtils.verifySm3WithSm2(msgByte, chnlSecretByte, signatureByte, pubKeyByte); + } + + + /** + * sm4加密 + * + * @param chnlId 渠道id + * @param sm4key 渠道sm4密钥 + * @param message 待加密报文 + * @return 加密后的报文内容 String + * @throws Exception + */ + public static String sm4Encrypt(String chnlId, String sm4key, String message) throws Exception { + // 用appId加密appSecret获取新秘钥 + byte[] appSecretEncData = EasyGmUtils.sm4Encrypt(chnlId.substring(0, 16).getBytes("UTF-8"), sm4key.getBytes("UTF-8")); + // 新秘钥串 + byte[] secKey = Hex.toHexString(appSecretEncData).toUpperCase().substring(0, 16).getBytes("UTF-8"); + // 加密数据 + return Hex.toHexString(EasyGmUtils.sm4Encrypt(secKey, message.getBytes("UTF-8"))).toUpperCase(); + } + + /** + * sm4解密 + * + * @param chnlId 渠道id + * @param sm4key 渠道sm4密钥 + * @param message 待解密报文 + * @return 解密后的报文 String + * @throws Exception + */ + public static String sm4Decrypt(String chnlId, String sm4key, String message) throws Exception { + // 生产解密key + byte[] appSecretEncDataDecode = EasyGmUtils.sm4Encrypt(chnlId.substring(0, 16).getBytes("UTF-8"), sm4key.getBytes("UTF-8")); + byte[] secKeyDecode = Hex.toHexString(appSecretEncDataDecode).toUpperCase().substring(0, 16).getBytes("UTF-8"); + return new String(EasyGmUtils.sm4Decrypt(secKeyDecode, Hex.decode(message))); + } + + private final static String version = "1.0.0"; + private final static String encType = "sm4"; + private final static String signType = "sm2"; + + /** + * 创建请求报文 + * + * @param chnlId 渠道id + * @param encData 加密的报文 + * @param signData 签名的报文 + * @param transType 请求接口名 + * @return + */ + public static JSONObject buildMsg(String chnlId, String encData, String signData, String transType) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("appId", chnlId); + jsonObject.put("encData", encData); + jsonObject.put("encType", encType); + jsonObject.put("signData", signData); + jsonObject.put("signType", signType); + jsonObject.put("timestamp", System.currentTimeMillis()); + jsonObject.put("transType", transType); + jsonObject.put("version", version); + return jsonObject; + } + + /** + * 创建和加密请求报文 + * + * @param chnlId 渠道id + * @param sm4key 渠道sm4密钥 + * @param prvkey 渠道私钥 + * @param transType 请求接口名 + * @param body 原始未加密的请求报文体 + * @return + * @throws Exception + */ + public static JSONObject encryptMsg(String chnlId, String sm4key, String prvkey, String transType, JSONObject body) throws Exception { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("appId", chnlId); + jsonObject.put("encType", encType); + jsonObject.put("data", body); + jsonObject.put("signType", signType); + jsonObject.put("timestamp", System.currentTimeMillis()); + jsonObject.put("transType", transType); + jsonObject.put("version", version); + // 加密后的报文 + String encData = sm4Encrypt(chnlId, sm4key, body.toJSONString()); + // 签名 + String signData = signature(jsonObject.toJSONString(), sm4key, prvkey); + jsonObject.fluentRemove("data"); + jsonObject.put("encData", encData); + jsonObject.put("signData", signData); + return jsonObject; + } + + /** + * 解密报文 + * + * @param jsonObject 医保电子凭证响应的原始加密报文 + * @param sm4key 渠道sm4密钥 + * @param pubKey 平台公钥 + * @param chnlIdSrc 渠道id(兼容旧中台报文返回参数无渠道id参数情况) + * @return + * @throws Exception + */ + public static String decryptMsg(JSONObject jsonObject, String sm4key, String pubKey, String chnlIdSrc) throws Exception { + String chnlId = (String) jsonObject.get("appId"); + chnlId = StringUtil.isEmpty(chnlId) ? chnlIdSrc : chnlId; + String msg = (String) jsonObject.get("encData"); + String message = (String) jsonObject.get("message"); + String code = (String) jsonObject.get("code"); + if (!"0".equals(code)) { + throw new RuntimeException(message); + } + // 解密 + String msgS = sm4Decrypt(chnlId, sm4key, msg); + // 验签 + String signData = (String) jsonObject.get("signData"); + boolean flag = verify(msgS, jsonObject.toJSONString(), signData, sm4key, pubKey); + if (!flag) { + throw new RuntimeException("验签失败!!!"); + } + return msgS; + } + + /** + * 移除json中空值的键值对 + * + * @param jsonObject + */ + private static void removeEmpty(JSONObject jsonObject) { + Iterator> it = jsonObject.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry entry = it.next(); + Object value = entry.getValue(); + if (value instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) value; + // 数组长度为0时将其处理,防止Gson转换异常 + if (jsonArray.size() == 0) { + it.remove(); + } else { + for (Object o : jsonArray) { + JSONObject asJsonObject = (JSONObject) o; + removeEmpty(asJsonObject); + } + } + } + if (value instanceof JSONObject) { + JSONObject asJsonObject = (JSONObject) value; + removeEmpty(asJsonObject); + } + if (value == null) { + it.remove(); + } + if (value instanceof String && StringUtil.isEmpty(value)) { + it.remove(); + } + } + } + + public static void main(String[] args) throws Exception { + + /** + * 生成sm2公私钥 + */ + // System.out.println(SM2Util.getKeyPairStr()); + + + /** + * 渠道id + */ + String chnlId = ""; + /** + * 渠道私钥 + */ + String prvkey = "TcuVE2p/="; + /** + * 渠道密钥 + */ + String sm4key = ""; + /** + * 平台公钥 + */ + String pubKey = "+++Vvnrxdp8CnkYFXBdu6c="; + + /** + * 报文体 + */ + JSONObject body = new JSONObject(); + body.put("appUserId", "ohNH9sgKsmJC3tR_spm9jcQx_bh"); + body.put("appId", chnlId); + body.put("idNo", ""); + body.put("idType", "01"); + body.put("userName", ""); + body.put("authCode", "111111"); + body.put("redirectURL", "www.baidu.com"); + body.put("phone", "18050860136"); + body.put("certificateStatus", "asdas"); + + + JSONObject s1 = encryptMsg(chnlId, sm4key, prvkey, "ec.gen.link", body); + System.out.println("加密后的报文:" + s1); + + String s2 = decryptMsg(s1, sm4key, pubKey, chnlId); + System.out.println("解密后的报文:" + s2); + + } + +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/utils/SignUtil.java b/src/main/java/com/dpkj/modules/chs/ynchs/utils/SignUtil.java new file mode 100644 index 0000000..05529da --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/utils/SignUtil.java @@ -0,0 +1,186 @@ +package com.dpkj.modules.chs.ynchs.utils; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import jodd.util.StringUtil; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; + + +public class SignUtil { + private static List ignoreSign = new ArrayList(); + + public static String getSignText(JSONObject jsonObject, String appSecret) { + Map signMap = new TreeMap(); + Set> entrys = jsonObject.entrySet(); + Iterator var4 = entrys.iterator(); + + + while (var4.hasNext()) { + Map.Entry entry = (Map.Entry) var4.next(); + if (!Optional.ofNullable(entry.getValue()).isPresent() && !ignoreSign.contains(entry.getKey())) { + signMap.put(entry.getKey(), getValue(entry.getValue())); + } + } + + ArrayList list = new ArrayList(); + Iterator var10 = signMap.entrySet().iterator(); + + while (var10.hasNext()) { + Map.Entry entry = (Map.Entry) var10.next(); + if (StringUtil.isNotEmpty(getObjString(entry.getValue()))) { + list.add((String) entry.getKey() + "=" + (String) entry.getValue() + "&"); + } + } + + int size = list.size(); + String[] arrayToSort = (String[]) list.toArray(new String[size]); + Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER); + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < size; ++i) { + sb.append(arrayToSort[i]); + } + + String signText = sb.append("key=").append(appSecret).toString(); + return signText; + } + + public static String getObjString(Object object) { + return object == null ? "" : (String) object; + } + + private static String getValue(Object value) { + return value instanceof String ? getObjString(value) : treeJsonParam(value); + } + + private static String treeJsonParam(Object value) { + String jsonParam = null; + if (value instanceof Map) { + Map treeNestedMap = new TreeMap(); + Map nestedMap = (Map) value; + Iterator var4 = nestedMap.entrySet().iterator(); + + while (var4.hasNext()) { + Map.Entry nestedEntry = (Map.Entry) var4.next(); + treeNestedMap.put((String) nestedEntry.getKey(), nestedEntry.getValue()); + } + + jsonParam = JSONObject.toJSONString(treeParams(treeNestedMap)); + } else if (value instanceof ArrayList) { + ArrayList ar = (ArrayList) value; + jsonParam = JSONObject.toJSONString(treeList(ar)); + } else if (value instanceof JSONArray) { + JSONArray jarr = (JSONArray) value; + jsonParam = JSONObject.toJSONString(treeJsonArray(jarr)); + } else { + jsonParam = value.toString(); + } + + return jsonParam; + } + + private static Map treeParams(Map params) { + if (params == null) { + return new TreeMap(); + } else { + Map treeParams = new TreeMap(); + Iterator var2 = params.entrySet().iterator(); + + while (true) { + while (var2.hasNext()) { + Map.Entry entry = (Map.Entry) var2.next(); + String key = (String) entry.getKey(); + Object value = entry.getValue(); + if (value instanceof Map) { + Map treeNestedMap = new TreeMap(); + Map nestedMap = (Map) value; + Iterator var8 = nestedMap.entrySet().iterator(); + + while (var8.hasNext()) { + Map.Entry nestedEntry = (Map.Entry) var8.next(); + treeNestedMap.put((String) nestedEntry.getKey(), nestedEntry.getValue()); + } + + treeParams.put(key, treeParams(treeNestedMap)); + } else if (value instanceof ArrayList) { + ArrayList ar = (ArrayList) value; + treeParams.put(key, treeList(ar)); + } else if (value instanceof JSONArray) { + JSONArray ar = (JSONArray) value; + treeParams.put(key, treeJsonArray(ar)); + } else if (!"".equals(value) && value != null) { + treeParams.put(key, value.toString()); + } + } + + return treeParams; + } + } + } + + private static JSONArray treeList(ArrayList list) { + if (list != null && list.size() != 0) { + JSONArray jsonArray = new JSONArray(); + int size = list.size(); + + for (int i = 0; i < size; ++i) { + jsonArray.add(i, list.get(i)); + } + + return treeJsonArray(jsonArray); + } else { + return null; + } + } + + private static JSONArray treeJsonArray(JSONArray jarr) { + if (jarr != null && jarr.size() != 0) { + JSONArray jsonArray = new JSONArray(); + int size = jarr.size(); + + for (int i = 0; i < size; ++i) { + Object value = jarr.get(i); + if (!(value instanceof Map)) { + if (value instanceof ArrayList) { + ArrayList ar = (ArrayList) value; + jsonArray.add(i, treeList(ar)); + } else if (value instanceof JSONArray) { + JSONArray ar = (JSONArray) value; + jsonArray.add(i, treeJsonArray(ar)); + } else if (!"".equals(value)) { + jsonArray.add(i, value.toString()); + } + } else { + Map treeNestedMap = new TreeMap(); + Map nestedMap = (Map) value; + Iterator var7 = nestedMap.entrySet().iterator(); + + while (var7.hasNext()) { + Map.Entry nestedEntry = (Map.Entry) var7.next(); + treeNestedMap.put((String) nestedEntry.getKey(), nestedEntry.getValue()); + } + + jsonArray.add(i, treeParams(treeNestedMap)); + } + } + + return jsonArray; + } else { + return null; + } + } + + static { + ignoreSign.add("signData"); + ignoreSign.add("encData"); + ignoreSign.add("extra"); + } +} diff --git a/src/main/java/com/dpkj/modules/chs/ynchs/utils/StringUtil.java b/src/main/java/com/dpkj/modules/chs/ynchs/utils/StringUtil.java new file mode 100644 index 0000000..b8ecdc8 --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/utils/StringUtil.java @@ -0,0 +1,430 @@ +package com.dpkj.modules.chs.ynchs.utils; + + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigDecimal; +import java.net.URLDecoder; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * @author: muweng + * @date: 2020/2/20 16:33 + * @description: Life and death can be ignored. + */ +public final class StringUtil { + public static final int XFF00 = 65280; + public static final int XF0 = 240; + public static final int X0F = 15; + public static final int XFF = 255; + public static final int X000000FF = 255; + public static final int X0000FF00 = 65280; + public static final int SIXTEEN = 16; + public static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; + + public StringUtil() { + } + + public static String getRandomString(int length) { + String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < length; ++i) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + + return sb.toString(); + } + + public static String getRandomNumber(int length) { + String base = "0123456789"; + Random random = new Random(); + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < length; ++i) { + int number = random.nextInt(base.length()); + sb.append(base.charAt(number)); + } + + return sb.toString(); + } + + public static int[] splitToIntArray(String s, String delim) { + String[] stringValueArray = split(s, delim); + int[] intValueArray = new int[stringValueArray.length]; + + for (int i = 0; i < intValueArray.length; ++i) { + intValueArray[i] = Integer.parseInt(stringValueArray[i]); + } + + return intValueArray; + } + + public static String[] split(String source, String div) { + int arynum = 0; + int div_length = div.length(); + int intIdx; + if (source.compareTo("") != 0) { + if (source.indexOf(div) != -1) { + intIdx = source.indexOf(div); + + for (int var6 = 1; source.indexOf(div, intIdx + div_length) != -1; arynum = var6++) { + intIdx = source.indexOf(div, intIdx + div_length); + } + + arynum += 2; + } else { + arynum = 1; + } + } else { + arynum = 0; + } + + String[] returnStr = new String[arynum]; + if (source.compareTo("") == 0) { + return returnStr; + } else if (source.indexOf(div) == -1) { + returnStr[0] = source.substring(0, source.length()); + return returnStr; + } else { + intIdx = source.indexOf(div); + returnStr[0] = source.substring(0, intIdx); + + int intCount; + for (intCount = 1; source.indexOf(div, intIdx + div_length) != -1; ++intCount) { + int intIdex = source.indexOf(div, intIdx + div_length); + returnStr[intCount] = source.substring(intIdx + div_length, intIdex); + intIdx = source.indexOf(div, intIdx + div_length); + } + + returnStr[intCount] = source.substring(intIdx + div_length, source.length()); + return returnStr; + } + } + + public static int doNullInt(String srcInt) { + return srcInt != null && !"".equals(srcInt) ? Integer.parseInt(srcInt) : 0; + } + + public static int doNullInt(Object obj) { + String srcInt = doNullStr(obj); + return srcInt != null && !"".equals(srcInt) ? Integer.parseInt(srcInt) : 0; + } + + public static long doNullLong(String srcInt) { + return srcInt != null && !"".equals(srcInt) ? Long.parseLong(srcInt) : 0L; + } + + public static long doNullLong(Object obj) { + String srcInt = doNullStr(obj); + return srcInt != null && !"".equals(srcInt) ? Long.parseLong(srcInt) : 0L; + } + + public static String doNullStr(Object obj) { + String str = ""; + if (obj != null) { + str = String.valueOf(obj); + if (str.equals("null")) { + str = ""; + } + } + + return str; + } + + public static Integer doNullInteger(Object obj) { + String str = doNullStr(obj); + if (isEmpty(str)) { + str = "0"; + } else { + int i = str.indexOf("."); + if (i > 0) { + str = str.substring(0, i); + } + } + + return Integer.valueOf(str); + } + + public static boolean isEmpty(String[] string) { + return string == null || string.length == 0; + } + + public static boolean isEmpty(String string) { + return string == null || "".equals(string.trim()) || "null".equals(string.trim()); + } + + public static boolean isNotEmpty(String string) { + return !isEmpty(string); + } + + public static boolean isEmpty(Object o) { + return o == null || "".equals(o); + } + + public static String padRight(String value, int totalWidth, char paddingChar) { + String temp = value; + if (value.length() > totalWidth) { + return value; + } else { + while (temp.length() < totalWidth) { + temp = temp + paddingChar; + } + + return temp; + } + } + + public static String padLeft(String value, int totalWidth, char paddingChar) { + String temp = value; + if (value.length() > totalWidth) { + return value; + } else { + while (temp.length() < totalWidth) { + temp = paddingChar + temp; + } + + return temp; + } + } + + public static String reTrimByString(String value) { + String reValue; + if (value != null && !value.equals("")) { + reValue = value.trim(); + } else { + reValue = ""; + } + + return reValue; + } + + public static String reTrimByObject(Object obj) { + String reValue; + if (obj != null && !obj.equals("")) { + reValue = String.valueOf(obj).trim(); + } else { + reValue = ""; + } + + return reValue; + } + + public static int indexOfStringArray(String[] strArr, String str) { + int index = -1; + if (strArr != null && str != null) { + for (int i = 0; i < strArr.length; ++i) { + if (str.equals(strArr[i])) { + index = i; + break; + } + } + } + + return index; + } + + public static String replaceFirst(String whole, String strold, String strnew) { + if (whole.indexOf(strold) > -1 && strnew != null) { + String whole_one = whole.substring(0, whole.indexOf(strold)); + String whole_two = whole.substring(whole.indexOf(strold) + strold.length()); + whole = whole_one + strnew + whole_two; + } + + return whole; + } + + public static Long[] convertionToLong(String[] strs) { + Long[] longs = null; + if (!isEmpty(strs)) { + longs = new Long[strs.length]; + + for (int i = 0; i < strs.length; ++i) { + String str = strs[i]; + long thelong = Long.valueOf(str).longValue(); + longs[i] = thelong; + } + } + + return longs; + } + + public static Long[] convertionToLongArr(String strs, String splitChar) { + if (isEmpty(splitChar)) { + splitChar = ","; + } + + Long[] result = null; + if (!isEmpty(strs)) { + String[] ids = strs.split(splitChar); + result = new Long[ids.length]; + + for (int i = 0; i < ids.length; ++i) { + result[i] = new Long(ids[i]); + } + } + + return result; + } + + public static String[] decodeStringToArray(String str, String div) { + ArrayList array = new ArrayList(); + StringTokenizer fenxi = new StringTokenizer(str, div); + + while (fenxi.hasMoreTokens()) { + String s1 = fenxi.nextToken(); + array.add(s1); + } + + String[] result = new String[array.size()]; + + for (int i = 0; i < result.length; ++i) { + result[i] = (String) array.get(i); + } + + return result; + } + + public static String convertionLongToString(Long[] l, String splitChar) { + String result = null; + if (l != null) { + result = Arrays.toString(l); + result = result.substring(1, result.length() - 1); + if (!isEmpty(splitChar)) { + result = result.replaceAll(",", splitChar); + } + } + + return result; + } + + public static String convertionObjectArrayToStr(Object[] strings, String regx) { + String result = null; + if (regx != null) { + result = Arrays.toString(strings); + result = result.substring(1, result.length() - 1); + if (!isEmpty(regx)) { + result = result.replaceAll(",", regx); + } + } + + return result; + } + + public static String charEncoding(String str) { + try { + str = URLDecoder.decode(str, "UTF-8"); + } catch (Exception var2) { + str = null; + } + + return str; + } + + public static String getStrTransMean(String str, String sregex, String sreplace) { + if (!isEmpty(str)) { + str = str.replaceAll(sregex, sreplace); + } + + return str; + } + + public static String replaceSpecialChar(String s) { + return s.replaceAll("/|\\\\|\\$|#|&|%|\\*|\\^|;|,|<|>|&|'|\"", ""); + } + + public static String replaceSpecialCode(String s) { + return null != s && !"".equals(s) ? s.replaceAll("<|>|\"|%|;|\\(|\\)|&|'|\\+|\\\\", "") : s; + } + + public static String getMapValue(Map map) { + StringBuffer str = new StringBuffer(); + Iterator it = map.values().iterator(); + + while (it.hasNext()) { + String val = String.valueOf(it.next()); + str.append(val); + } + + return str.toString(); + } + + public static String bSubstring(String s, int length) { + try { + byte[] bytes = s.getBytes("Unicode"); + int n = 0; + + int i; + for (i = 2; i < bytes.length && n < length; ++i) { + if (i % 2 == 1) { + ++n; + } else if (bytes[i] != 0) { + ++n; + } + } + + if (i % 2 == 1) { + if (bytes[i - 1] != 0) { + --i; + } else { + ++i; + } + } + + return new String(bytes, 0, i, "Unicode"); + } catch (Exception var5) { + return new String(""); + } + } + + public static String getString(Object o) { + return o == null ? "" : o.toString(); + } + + public static Properties getProperties(String filename) { + Properties properties = new Properties(); + InputStream in = null; + in = StringUtil.class.getClassLoader().getResourceAsStream(filename); + + try { + properties.load(in); + return properties; + } catch (IOException var4) { + return properties; + } + } + + public static String dateTostr(Date date, String format) throws Exception { + SimpleDateFormat dateFormat = new SimpleDateFormat(format); + String dateStr = null; + dateStr = dateFormat.format(date); + return dateStr; + } + + public static String iso8859ToUTF8(Object obj) { + try { + return obj == null ? "" : new String(obj.toString().getBytes("UTF-8"), "iso-8859-1"); + } catch (Exception var2) { + return ""; + } + } + + public static String utf8ToIso8859(Object obj) { + try { + return obj == null ? "" : new String(obj.toString().getBytes("iso-8859-1"), "UTF-8"); + } catch (Exception var2) { + return ""; + } + } + + public static BigDecimal getStrBigDecimal(String str) { + return isEmpty(str) ? null : new BigDecimal(str); + } + + public static String cutString(String str, int size) { + return isNotEmpty(str) && str.length() > 100 ? str.substring(0, 100) : str; + } +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index bcf2c3d..067a24c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -49,18 +49,18 @@ dpkj: # 医保机构编码 org-id: H53082800070 # 收款员-编号 - operator-id: 0001 + operator-id: 2998 # 收款员-姓名 - operator-name: admin + operator-name: 银医自助缴费 # 科室-编号 - office-id: 00031 + office-id: 71 # 科室-名称 - office-name: 门诊 + office-name: 财务科 # 云南省级医保配置 yn: # 省局医保接口地址 - # url: http://ldjk.yn.hsip.gov.cn/eapdomain/org/local/api/hos/uldFeeInfo - url: http://ldjk.yn.hsip.gov.cn/eapdomain/callService + # url: http://10.114.177.55:8080 + url: http://ldjk.yn.hsip.gov.cn # 医保机构编码 fixme-dins-code: H53082800070 # 医保机构名称 @@ -68,9 +68,22 @@ dpkj: # 服务商统一社会信用代码 info-sys-code: 9150000020285539XU # 服务商ID码 + # info-sys-sign: neu0fe98564e42458f9c86377e10e20c info-sys-sign: 7f6fa92af64a403eb871a3c800cb6946 # 城市编码 city-code: 530800 + # 应用ID + appid: 1IMPBSBC50OO4460C80A00001C356F13 + # 应用密钥 + app-key: 1IMPBSBC50OP4460C80A00004D665784 + # 应用名称 + app-name: 澜沧县中医医院手机自助平台 + # 渠道私钥 + channel-private-key: ALwGHGDcx/UEQiOYHkkym/KCjpDZGfj7AVRV3sxewJX5 + # 渠道公钥 + channel-public-key: BIAUk7awZVtRnd6GXqHTvTak2r3Wg6P7jWsKbBNkbSlephiS1ASWt2yFwpyWxMsqtJ59MDebkfLLDQdB/kfYxEY= + # 平台公钥 + platform-public-key: BPhYoS7Bjo3MRx7I4dbtpa/dR+Na8kMY8F8yzEmlWRQjICAp0tYyfiqPt50N3NVFEijz2DJ0QY9FLCFDEdhnPvY= file: # 文件保存地址 diff --git a/src/main/resources/application-pro.yml b/src/main/resources/application-pro.yml index ff3c00d..cfcae7f 100644 --- a/src/main/resources/application-pro.yml +++ b/src/main/resources/application-pro.yml @@ -48,13 +48,17 @@ dpkj: # 云南省级医保配置 yn: # 省局医保接口地址 - url: http://ldjk.yn.hsip.gov.cn/eapdomain/org/local/api/hos/uldFeeInfo + url: http://ldjk.yn.hsip.gov.cn # 医保机构编码 - fixmedins-code: H53082800070 + fixme-dins-code: H53082800070 + # 医保机构名称 + fixme-dins-name: 澜沧拉祜族自治县中医医院 # 服务商统一社会信用代码 - infosyscode: 9150000020285539XU + info-sys-code: 9150000020285539XU # 服务商ID码 - infosyssign: 7f6fa92af64a403eb871a3c800cb6946 + info-sys-sign: 7f6fa92af64a403eb871a3c800cb6946 + # 城市编码 + city-code: 530800 file: # 文件保存地址 path: D:\Project\Express\upload