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