对接云南省局医保

This commit is contained in:
2025-10-25 19:05:34 +08:00
parent 71d0ae1fd8
commit 108e6c4152
24 changed files with 2082 additions and 401 deletions

View File

@@ -3,4 +3,6 @@ echo off
:: 添加jar包到本地仓库 :: 添加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=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 pause

Binary file not shown.

View File

@@ -179,6 +179,13 @@
<artifactId>itext-asian</artifactId> <artifactId>itext-asian</artifactId>
<version>5.2.0</version> <version>5.2.0</version>
</dependency> </dependency>
<!--<dependency>
<groupId>hsaf.common</groupId>
<artifactId>hsaf</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>-->
</dependencies> </dependencies>
<build> <build>

View File

@@ -20,7 +20,7 @@ public class YnChsConfig {
private String url; private String url;
/** /**
* 医保机构编码 * 两定医保机构编码
*/ */
private String fixmeDinsCode; private String fixmeDinsCode;
/** /**
@@ -43,5 +43,30 @@ public class YnChsConfig {
*/ */
private String cityCode; private String cityCode;
/**
* 应用ID
*/
private String appid;
/**
* 应用密钥
*/
private String appKey;
/**
* 应用名称
*/
private String appName;
/**
* 渠道私钥
*/
private String channelPrivateKey;
/**
* 渠道公钥
*/
private String channelPublicKey;
/**
* 平台公钥
*/
private String platformPublicKey;
} }

View File

@@ -9,6 +9,7 @@ public interface CharsetConst {
// 中文编码 // 中文编码
String GB2312 = "GB2312"; String GB2312 = "GB2312";
// UTF8
String UTF8 = "UTF-8";
} }

View File

@@ -29,17 +29,16 @@ public class HispayController {
private final IHispayService hispayService; private final IHispayService hispayService;
/** /**
* 通过医保卡或医保电子凭证读卡 * 通过医保卡或医保电子凭证读卡
* *
* @return com.dpkj.common.vo.Result<?> * @return com.dpkj.common.vo.Result<?>
* @author 萧道子 2025/5/20 * @author 萧道子 2025/5/20
*/ */
@PostMapping("findReadCode") @PostMapping("getUserInfoByChsCode")
public Result<?> findReadCode() { public Result<?> getUserInfoByChsCode() {
try { try {
JSONObject res = hispayService.readCode(); JSONObject res = hispayService.getUserInfoByChsCode();
return Result.ok("成功", res); return Result.ok("成功", res);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
@@ -56,15 +55,15 @@ public class HispayController {
* @return com.dpkj.common.vo.Result<?> * @return com.dpkj.common.vo.Result<?>
* @author 萧道子 2025/5/28 * @author 萧道子 2025/5/28
*/ */
@PostMapping("findReadCard") @PostMapping("getUserInfoByChsCard")
public Result<?> findReadCard(@RequestBody JSONObject data) { public Result<?> getUserInfoByChsCard(@RequestBody JSONObject data) {
try { try {
String password = data.getString("password"); String password = data.getString("password");
if (StrUtil.isEmpty(password)) { if (StrUtil.isEmpty(password)) {
throw new RuntimeException("密码不可为空"); throw new RuntimeException("密码不可为空");
} }
JSONObject res = hispayService.readCard(password); JSONObject res = hispayService.getUserInfoByChsCard(password);
return Result.ok("成功", res); return Result.ok("成功", res);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -8,21 +8,20 @@ import com.dpkj.modules.chs.hischs.vo.OutpatientFinalModel;
public interface IHispayService { public interface IHispayService {
/** /**
* 通过医保电子凭证读卡 * 通过医保电子凭证获-取患者身份信息
* *
* @return com.alibaba.fastjson.JSONObject * @return com.alibaba.fastjson.JSONObject
* @author 萧道子 2025/5/28 * @author 萧道子 2025/5/28
*/ */
JSONObject readCode(); JSONObject getUserInfoByChsCode();
/** /**
* 通过医保卡-读卡 * 通过医保卡-取患者身份信息
* *
* @return com.alibaba.fastjson.JSONObject * @return com.alibaba.fastjson.JSONObject
* @author 萧道子 2025/5/28 * @author 萧道子 2025/5/28
*/ */
JSONObject readCard(String password); JSONObject getUserInfoByChsCard(String password);
/** /**

View File

@@ -15,7 +15,6 @@ import com.jacob.com.Dispatch;
import com.jacob.com.Variant; import com.jacob.com.Variant;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import java.util.Map; import java.util.Map;
@@ -69,6 +68,150 @@ public class HispayServiceImpl implements IHispayService {
// ComThread.Release(); //容易导致线程发生阻塞 // 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 * @return java.lang.String
* @author 萧道子 2025/5/27 * @author 萧道子 2025/5/27
*/ */
private String processParameters(JSONObject params, String password) { private String handleParams(JSONObject params, String password) {
JSONObject req = new JSONObject() {{ JSONObject req = new JSONObject() {{
put("timestamp", ""); // 请求发送时间 DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss") put("timestamp", ""); // 请求发送时间 DateUtil.format(DateUtil.date(), "yyyyMMddHHmmss")
put("requestid", ""); // 唯一请求id IDGenerator.getSnowflakeIdToStr() put("requestid", ""); // 唯一请求id IDGenerator.getSnowflakeIdToStr()
@@ -85,8 +228,11 @@ public class HispayServiceImpl implements IHispayService {
put("password", password); // 密码 put("password", password); // 密码
put("params", params); put("params", params);
}}; }};
Document document = XmlUtil.mapToXml(req, "request"); return this.JsonObjectToXml(req);
return XmlUtil.toStr(document, "UTF-8", false, true); }
private String handleParams(JSONObject params) {
return handleParams(params, null);
} }
@@ -98,7 +244,6 @@ public class HispayServiceImpl implements IHispayService {
* @author 萧道子 2025/5/28 * @author 萧道子 2025/5/28
*/ */
private JSONObject verifyResult(String resStr) { private JSONObject verifyResult(String resStr) {
if (StrUtil.isBlank(resStr)) { if (StrUtil.isBlank(resStr)) {
throw new RuntimeException("信息获取失败"); throw new RuntimeException("信息获取失败");
} }
@@ -119,149 +264,15 @@ public class HispayServiceImpl implements IHispayService {
} }
@Override /**
public JSONObject readCode() { * 将jsonobject转为xml
/** 1、组装参数 */ *
JSONObject val = new JSONObject() * @param val :
.fluentPut("cardtype", "9") // 1.就诊卡2.医保卡5.门诊号6.患者姓名和电话号码8.电子健康码/卡9.医保电子凭证 * @return java.lang.String
.fluentPut("cardno", "") // 患者就诊卡号 * @author 萧道子 2025/10/25
.fluentPut("sfzh", "") // 身份证号 */
.fluentPut("hzxm", "") // 患者姓名 private String JsonObjectToXml(JSONObject val) {
.fluentPut("phone", ""); // 患者电话号码 return XmlUtil.toStr(XmlUtil.mapToXml(val, "request"), "UTF-8", false, true);
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");
} }
@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);
}
} }

View File

@@ -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;
}

View File

@@ -1,8 +1,9 @@
package com.dpkj.modules.chs.ynchs.controller; package com.dpkj.modules.chs.ynchs.controller;
import com.alibaba.fastjson.JSONObject;
import com.dpkj.common.vo.Result; 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.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
@@ -20,18 +21,21 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/chs/yn") @RequestMapping("/chs/yn")
public class YnChsPayController { public class YnChsPayController {
private final IYnChsPayService ynChsPayService; private final IYnchspayByWebapiService ynchspayByWebapiService;
private final IYnchspayByDllService ynchspayByDllService;
/** /**
* 获取支付凭证 * 刷脸获取身份信息
* *
* @return com.dpkj.common.vo.Result<?> * @return com.dpkj.common.vo.Result<?>
* @author 萧道子 2025/10/22 * @author 萧道子 2025/10/24
*/ */
@PostMapping("getPayToken") @PostMapping("getUserByFace")
public Result<?> getPayToken() { public Result<?> getUserByFace() {
try { try {
JSONObject result = ynChsPayService.getPayToken(); ChsCheckResponse result = ynchspayByDllService.getUserByFace();
return Result.ok("成功", result); return Result.ok("成功", result);
} catch (Exception e) { } catch (Exception e) {
return Result.error(e.getMessage()); return Result.error(e.getMessage());

View File

@@ -2,7 +2,6 @@ package com.dpkj.modules.chs.ynchs.dll;
import com.sun.jna.Library; import com.sun.jna.Library;
import com.sun.jna.Native; import com.sun.jna.Native;
import com.sun.jna.Pointer;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
@@ -21,8 +20,8 @@ public class YnChsPayDll {
*/ */
public static EcDll instance() throws DllRegistrationException { public static EcDll instance() throws DllRegistrationException {
try { try {
// return Native.load("CHSInterfaceYn.dll", EcDll.class); return Native.load("CHSInterfaceYn.dll", EcDll.class);
return Native.load("D:\\Code\\CHS\\CHSInterfaceYn.dll", EcDll.class); // return Native.load("D:\\Code\\CHS\\CHSInterfaceYn.dll", EcDll.class);
} catch (UnsatisfiedLinkError e) { } catch (UnsatisfiedLinkError e) {
log.info("[YnChsPayDll][instance][云南省级医保动态库] SDK注册失败{}", e.getMessage()); log.info("[YnChsPayDll][instance][云南省级医保动态库] SDK注册失败{}", e.getMessage());
throw new DllRegistrationException("Failed to load YnChsPayDll library: ", e); throw new DllRegistrationException("Failed to load YnChsPayDll library: ", e);
@@ -62,7 +61,7 @@ public class YnChsPayDll {
* @return int * @return int
* @author 萧道子 2025/10/22 * @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 * @return int
* @author 萧道子 2025/10/22 * @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 * @return int
* @author 萧道子 2025/10/22 * @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);
} }

View File

@@ -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();
}

View File

@@ -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();
}

View File

@@ -0,0 +1,12 @@
package com.dpkj.modules.chs.ynchs.service;
/**
* @Auther: 萧道子
* @Date: 2025/8/7 14:45
* @Description:
*/
public interface IYnchspayByWebapiService {
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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("医保接口请求失败!");
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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
* <p>
* 用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 <b>asn1 format</b>
*/
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 <b>asn1 format</b>
* @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));
}
}

View File

@@ -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<Map.Entry<String, Object>> it = jsonObject.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> 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);
}
}

View File

@@ -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<String> ignoreSign = new ArrayList();
public static String getSignText(JSONObject jsonObject, String appSecret) {
Map<String, String> signMap = new TreeMap();
Set<Map.Entry<String, Object>> entrys = jsonObject.entrySet();
Iterator var4 = entrys.iterator();
while (var4.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry) var4.next();
if (!Optional.ofNullable(entry.getValue()).isPresent() && !ignoreSign.contains(entry.getKey())) {
signMap.put(entry.getKey(), getValue(entry.getValue()));
}
}
ArrayList<String> list = new ArrayList();
Iterator var10 = signMap.entrySet().iterator();
while (var10.hasNext()) {
Map.Entry<String, String> 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<String, Object> 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<String, Object> treeParams(Map<String, Object> params) {
if (params == null) {
return new TreeMap();
} else {
Map<String, Object> treeParams = new TreeMap();
Iterator var2 = params.entrySet().iterator();
while (true) {
while (var2.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry) var2.next();
String key = (String) entry.getKey();
Object value = entry.getValue();
if (value instanceof Map) {
Map<String, Object> 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<String, Object> 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");
}
}

View File

@@ -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<String> 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<String, Object> 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;
}
}

View File

@@ -49,18 +49,18 @@ dpkj:
# 医保机构编码 # 医保机构编码
org-id: H53082800070 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: yn:
# 省局医保接口地址 # 省局医保接口地址
# url: http://ldjk.yn.hsip.gov.cn/eapdomain/org/local/api/hos/uldFeeInfo # url: http://10.114.177.55:8080
url: http://ldjk.yn.hsip.gov.cn/eapdomain/callService url: http://ldjk.yn.hsip.gov.cn
# 医保机构编码 # 医保机构编码
fixme-dins-code: H53082800070 fixme-dins-code: H53082800070
# 医保机构名称 # 医保机构名称
@@ -68,9 +68,22 @@ dpkj:
# 服务商统一社会信用代码 # 服务商统一社会信用代码
info-sys-code: 9150000020285539XU info-sys-code: 9150000020285539XU
# 服务商ID码 # 服务商ID码
# info-sys-sign: neu0fe98564e42458f9c86377e10e20c
info-sys-sign: 7f6fa92af64a403eb871a3c800cb6946 info-sys-sign: 7f6fa92af64a403eb871a3c800cb6946
# 城市编码 # 城市编码
city-code: 530800 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: file:
# 文件保存地址 # 文件保存地址

View File

@@ -48,13 +48,17 @@ dpkj:
# 云南省级医保配置 # 云南省级医保配置
yn: 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码 # 服务商ID码
infosyssign: 7f6fa92af64a403eb871a3c800cb6946 info-sys-sign: 7f6fa92af64a403eb871a3c800cb6946
# 城市编码
city-code: 530800
file: file:
# 文件保存地址 # 文件保存地址
path: D:\Project\Express\upload path: D:\Project\Express\upload