diff --git a/src/main/java/com/dpkj/common/config/YnChsConfig.java b/src/main/java/com/dpkj/common/config/YnChsConfig.java index e1b81b2..5a23c5b 100644 --- a/src/main/java/com/dpkj/common/config/YnChsConfig.java +++ b/src/main/java/com/dpkj/common/config/YnChsConfig.java @@ -22,17 +22,26 @@ public class YnChsConfig { /** * 医保机构编码 */ - private String fixmedinsCode; + private String fixmeDinsCode; + /** + * 医保机构名称 + */ + private String fixmeDinsName; /** * 服务商统一社会信用代码 */ - private String infosyscode; + private String infoSysCode; /** * 服务商ID码 */ - private String infosyssign; + private String infoSysSign; + + /** + * 医保区划 + */ + private String cityCode; } diff --git a/src/main/java/com/dpkj/common/constant/CharsetConst.java b/src/main/java/com/dpkj/common/constant/CharsetConst.java new file mode 100644 index 0000000..a4a1a35 --- /dev/null +++ b/src/main/java/com/dpkj/common/constant/CharsetConst.java @@ -0,0 +1,14 @@ +package com.dpkj.common.constant; + +/** + * @Auther: 萧道子 + * @Date: 2025/10/22 19:59 + * @Description: + */ +public interface CharsetConst { + + // 中文编码 + String GB2312 = "GB2312"; + + +} diff --git a/src/main/java/com/dpkj/modules/chs/padchs/dll/PadChsPayDll.java b/src/main/java/com/dpkj/modules/chs/padchs/dll/PadChsPayDll.java index 24e4712..50d1a9a 100644 --- a/src/main/java/com/dpkj/modules/chs/padchs/dll/PadChsPayDll.java +++ b/src/main/java/com/dpkj/modules/chs/padchs/dll/PadChsPayDll.java @@ -21,7 +21,7 @@ public class PadChsPayDll { */ public static EcDll instance() throws DllRegistrationException { try { - EcDll dll = Native.load("NationECCode", EcDll.class); + EcDll dll = Native.load("NationECCode.dll", EcDll.class); // 加载动态库 /*Native.load("libeay32", EcDll.class); diff --git a/src/main/java/com/dpkj/modules/chs/padchs/service/impl/PadChsPayServiceImpl.java b/src/main/java/com/dpkj/modules/chs/padchs/service/impl/PadChsPayServiceImpl.java index ca79aac..ec8a286 100644 --- a/src/main/java/com/dpkj/modules/chs/padchs/service/impl/PadChsPayServiceImpl.java +++ b/src/main/java/com/dpkj/modules/chs/padchs/service/impl/PadChsPayServiceImpl.java @@ -6,6 +6,7 @@ import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.TypeReference; import com.dpkj.common.config.PadChsConfig; +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; @@ -83,7 +84,7 @@ public class PadChsPayServiceImpl implements IPadChsPayService { // 调用接口 Pointer resp = new Memory(1024 * 10); String resultStr = dll.NationEcTrans(chsPadConfig.getUrl(), request.toString(), resp); - String response = resp.getString(0, "GB2312"); + String response = resp.getString(0, CharsetConst.GB2312); DateTime responseTime = DateUtil.date(); log.debug("[PadChsPayServiceImpl][getInfoByQrcode][终端医保电子凭证码解码接口] 响应:{} 出参:{}", resultStr, response); @@ -138,7 +139,7 @@ public class PadChsPayServiceImpl implements IPadChsPayService { // 调用接口 Pointer resp = new Memory(1024 * 10); String resultStr = dll.NationEcTrans(chsPadConfig.getUrl(), request.toString(), resp); - String response = resp.getString(0, "GB2312"); + String response = resp.getString(0, CharsetConst.GB2312); DateTime responseTime = DateUtil.date(); log.debug("[PadChsPayServiceImpl][chsAuth][刷脸获取医保用户身份授权接口] 响应:{} 出参:{}", resultStr, response); @@ -187,7 +188,7 @@ public class PadChsPayServiceImpl implements IPadChsPayService { // 调用接口 Pointer resp = new Memory(1024 * 10); String resultStr = dll.NationEcTrans(chsPadConfig.getUrl(), request.toString(), resp); - String response = resp.getString(0, "GB2312"); + String response = resp.getString(0, CharsetConst.GB2312); DateTime responseTime = DateUtil.date(); log.debug("[PadChsPayServiceImpl][chsCheck][刷脸授权获取医保身份接口] 响应:{} 出参:{}", resultStr, response); 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 new file mode 100644 index 0000000..007d0ce --- /dev/null +++ b/src/main/java/com/dpkj/modules/chs/ynchs/controller/YnChsPayController.java @@ -0,0 +1,40 @@ +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 lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @Auther: 萧道子 + * @Date: 2025/10/22 16:05 + * @Description: + */ +@Slf4j +@AllArgsConstructor +@RestController +@RequestMapping("/chs/yn") +public class YnChsPayController { + + private final IYnChsPayService ynChsPayService; + + /** + * 获取支付凭证 + * + * @return com.dpkj.common.vo.Result + * @author 萧道子 2025/10/22 + */ + @PostMapping("getPayToken") + public Result getPayToken() { + try { + JSONObject result = ynChsPayService.getPayToken(); + 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 5265534..b4e85cf 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 @@ -21,7 +21,8 @@ public class YnChsPayDll { */ public static EcDll instance() throws DllRegistrationException { try { - return Native.load("CHSInterfaceYn", 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); @@ -47,16 +48,53 @@ public class YnChsPayDll { */ public interface EcDll extends Library { + /** - * 交易接口调用函数 + * 初始化函数 + * 检查整个运行环境:包括网络环境、运行所需文件、参数等的检查。 + * 成功后显示医疗机构信息、服务商信息,业务经办人需要确认这些信息,避免数据造成错乱带来的损失。 * - * @param strUrl : 业务请求地址 - * @param InData : 交易请求数据 - JSON格式字符串 - * @param OutData : 交易返回数据 - code为0时,交易成功;否则返回错误信息说明 - * @return java.lang.String 返回字符串 0000 为动态库调用成功 - * @author 萧道子 2025/7/30 + * @param fixmeDinsCode : + * @param infoSysCode : + * @param infoSysSign : + * @param url : + * @param errMsg : + * @return int + * @author 萧道子 2025/10/22 */ - String NationEcTrans(String strUrl, String InData, Pointer OutData); + int Init(String fixmeDinsCode, String infoSysCode, String infoSysSign, String url, Pointer errMsg); + + + /** + * 通用业务函数 + * HIS 系统服务商需要向医保中心发送业务请求的通用函数 + * + * @param fixmeDinsCode : + * @param infoSysCode : + * @param infoSysSign : + * @param inputData : 请求参数 + * @param outputData : 响应结果 + * @param errMsg : + * @return int + * @author 萧道子 2025/10/22 + */ + int BusinessHandle(String fixmeDinsCode, String infoSysCode, String infoSysSign, String inputData, Pointer outputData, Pointer errMsg); + + + /** + * 通用业务函数 + * HIS 系统服务商需要向医保中心发送业务请求的通用函数 输入和输出的字符集编码格式是 UTF-8 + * + * @param fixmeDinsCode : + * @param infoSysCode : + * @param infoSysSign : + * @param inputData : 请求参数 + * @param outputData : 响应结果 + * @param errMsg : + * @return int + * @author 萧道子 2025/10/22 + */ + int BusinessHandleW(String fixmeDinsCode, String infoSysCode, String infoSysSign, String inputData, Pointer outputData, Pointer 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 index f0deb16..f6dabcd 100644 --- a/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnChsPayService.java +++ b/src/main/java/com/dpkj/modules/chs/ynchs/service/IYnChsPayService.java @@ -1,9 +1,30 @@ 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/impl/YnChsPayServiceImpl.java b/src/main/java/com/dpkj/modules/chs/ynchs/service/impl/YnChsPayServiceImpl.java index 6a45975..28489f8 100644 --- 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 @@ -1,8 +1,23 @@ 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; @@ -22,6 +37,8 @@ public class YnChsPayServiceImpl implements IYnChsPayService { @Autowired private YnChsConfig ynChsConfig; + @Autowired + private IPadChsPayService padChsPayService; @PostConstruct public void postConstruct() { @@ -32,11 +49,137 @@ public class YnChsPayServiceImpl implements IYnChsPayService { log.error("[YnChsPayServiceImpl][postConstruct][云南省局医保DLL] 加载失败:{}", e.getMessage()); throw new RuntimeException("云南省局医保DLL库加载失败:" + e.getMessage(), e); } - initPrinter(); } - private void initPrinter() { - // TODO 萧道子 2025/7/31 : + @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/resources/application-dev.yml b/src/main/resources/application-dev.yml index 2890330..bcf2c3d 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,5 +1,5 @@ server: - port: 15948 + port: 15946 servlet: context-path: /api tomcat: @@ -59,13 +59,19 @@ dpkj: # 云南省级医保配置 yn: # 省局医保接口地址 - url: http://ldjk.yn.hsip.gov.cn/eapdomain/org/local/api/hos/uldFeeInfo + # url: http://ldjk.yn.hsip.gov.cn/eapdomain/org/local/api/hos/uldFeeInfo + url: http://ldjk.yn.hsip.gov.cn/eapdomain/callService # 医保机构编码 - 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: G:\Temp\img